Simple, scalable state management.

Overview

logo

MobX

Simple, scalable state management.

npm version OpenCollective OpenCollective

Discuss on Github View changelog


Documentation for older unsupported V4/V5 can be found here, but be sure to read about current documentation first.

MobX is made possible by the generosity of the sponsors below, and many other individual backers. Sponsoring directly impacts the longevity of this project.

🥇 Gold sponsors ($3000+ total contribution):
Mendix Frontend Masters Facebook Open Source Auction Frontier Guilded Coinbase Canva

🥈 Silver sponsors ($100+ pm):
CodeFirst DCSL Software Bugsnag Curology Modulz Space307

🥉 Bronze sponsors ($500+ total contributions):
mantro GmbH Algolia talentplot DAZN Blokt


Introduction

Anything that can be derived from the application state, should be. Automatically.

MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming (TFRP). The philosophy behind MobX is simple:

😙
Straightforward

Write minimalistic, boilerplate free code that captures your intent. Trying to update a record field? Use the good old JavaScript assignment. Updating data in an asynchronous process? No special tools are required, the reactivity system will detect all your changes and propagate them out to where they are being used.

🚅
Effortless optimal rendering

All changes to and uses of your data are tracked at runtime, building a dependency tree that captures all relations between state and output. This guarantees that computations depending on your state, like React components, run only when strictly needed. There is no need to manually optimize components with error-prone and sub-optimal techniques like memoization and selectors.

🤹🏻‍♂️
Architectural freedom

MobX is unopinionated and allows you to manage your application state outside of any UI framework. This makes your code decoupled, portable, and above all, easily testable.

A quick example

So what does code that uses MobX look like?

import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"

// Model the application state.
class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increase() {
        this.secondsPassed += 1
    }

    reset() {
        this.secondsPassed = 0
    }
}

const myTimer = new Timer()

// Build a "user interface" that uses the observable state.
const TimerView = observer(({ timer }) => (
    <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))

ReactDOM.render(<TimerView timer={myTimer} />, document.body)

// Update the 'Seconds passed: X' text every second.
setInterval(() => {
    myTimer.increase()
}, 1000)

The observer wrapper around the TimerView React component, will automatically detect that rendering depends on the timer.secondsPassed observable, even though this relationship is not explicitly defined. The reactivity system will take care of re-rendering the component when precisely that field is updated in the future.

Every event (onClick / setInterval) invokes an action (myTimer.increase / myTimer.reset) that updates observable state (myTimer.secondsPassed). Changes in the observable state are propagated precisely to all computations and side effects (TimerView) that depend on the changes being made.

MobX unidirectional flow

This conceptual picture can be applied to the above example, or any other application using MobX.

To learn about the core concepts of MobX using a larger example, check out The gist of MobX section, or take the 10 minute interactive introduction to MobX and React. The philosophy and benefits of the mental model provided by MobX are also described in great detail in the blog posts UI as an afterthought and How to decouple state and UI (a.k.a. you don’t need componentWillMount).

What others are saying...

Guise, #mobx isn't pubsub, or your grandpa's observer pattern. Nay, it is a carefully orchestrated observable dimensional portal fueled by the power cosmic. It doesn't do change detection, it's actually a level 20 psionic with soul knife, slashing your viewmodel into submission.

After using #mobx for lone projects for a few weeks, it feels awesome to introduce it to the team. Time: 1/2, Fun: 2X

Working with #mobx is basically a continuous loop of me going “this is way too simple, it definitely won’t work” only to be proven wrong

I have built big apps with MobX already and comparing to the one before that which was using Redux, it is simpler to read and much easier to reason about.

The #mobx is the way I always want things to be! It's really surprising simple and fast! Totally awesome! Don't miss it!

Further resources and documentation

The MobX book

Created by Pavan Podila and Michel Weststrate.

Videos

And an all around MobX awesome list.

Credits

MobX is inspired by reactive programming principles as found in the spreadsheets. It is inspired by MVVM frameworks like MeteorJS tracker, knockout and Vue.js, but MobX brings Transparent Functional Reactive Programming to the next level and provides a standalone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.

A ton of credits goes to Mendix, for providing the flexibility and support to maintain MobX and the chance to proof the philosophy of MobX in a real, complex, performance critical applications.

Comments
  • Proposal: Improve automatic conversion to observables

    Proposal: Improve automatic conversion to observables

    This is a proposal to kill the automatic conversion of objects / arrays to observables:

    const todo = { done: false}
    isObservable(todo) // false
    
    const todos = observable([]) // observable array
    todos.push(todo)
    isObservable(todo) // true
    

    1. Stop automatic conversion of observables

    While this, on one hand, is very cool and convenient for beginners, because any changes deep inside the todos collection can automatically be tracked. It also is a source of confusion. Although not often a cause of trouble, the mutation-of-the-righ-hand-side-of-an assignment (or push in the above example) is weird and hard to track if you didn't expect it. Several people already run into that. See for example #644

    So after this proposal one would need to do the following:

    const todo = observable({ done: false})
    isObservable(todo) // true
    
    const todos = observable([])
    todos.push(todo)
    isObservable(todo) // true
    

    2. Kill modifiers

    Currently, MobX provides an opt out mechanism of this behavior, modifiers like asFlat and asReference.

    For example modifying the original listing to todos = observable(asFlat([])) would have prevented the behavior. So it can be opted-out. Since this proposal removes automagic observable conversion, the need to know about these modifiers is gone and they can be removed. (computed, asMap and asStructure are still useful modifiers though). This solves a lot of design questions about the most convenient api around modifiers. See for example #211, #197, #56

    3. observable(object) should clone instead of enhance

    const todo1 = { done: false }
    const todo2 = observable(todo1)
    todo1 === todo2 // true
    
    const todos1 = []
    const todos2 = observable(todos1)
    todos1 === todos2 // false
    

    The above example shows a nice inconsistency between observable objects and arrays (and maps). Objects being converted keep their identity, while arrays produce a fresh object (originally due to limitations in ES5). However to keep things explicit and predictable, I think it is nicer to prevent objects from being enhanced in place, and instead produce fresh objects. In other words, currently observable(object) is an alias for extendObservable(object, object). With this proposal observable(object) would be the same as extendObservable({}, object)

    4. Migration path / questions

    1. There should be a mobx version that can logs where in the current code base automatic conversion is applied, so that before moving to the next mobx version these cases can be updated
    2. @observable decorator is barely effected. Except when assigning objects, arrays. Should those be converted automatically to observables one level deep? E.g. should one need to write @observable todos = observable([]) or should @observable todos = [] suffice (and convert []) still automatically? Or should the whole api be more more explicit: @observable todos = observableArray() ?
    3. calling observable on an already observable thing should probably warn

    TL; DR

    I think this proposal reduces the perceived level of magic introduced by MobX, as the developer is now completely in control when objects become observable and has to be explicit about this. This might reduce the wow-factor for beginners a little bit, but reduce the confusion for intermediate (and beginner) MobX users.

    This is definitely a breaking change, so run time guidance on all cases where behavior is changed would be very important I think (unless api method names are also changed, in that case old behavior could live side by side for a while)

    💬 discuss 
    opened by mweststrate 158
  • 🚀 Proposal: MobX 6: 🧨drop decorators,😱unify ES5 and proxy implementations, 💪smaller bundle

    🚀 Proposal: MobX 6: 🧨drop decorators,😱unify ES5 and proxy implementations, 💪smaller bundle

    MobX 6

    Hi folks, I've tinkered a lot about MobX 6 lately, so I want to layout the vision I have currently

    Goals

    🧨 1. Become compatible with modern ES standards

    Let's start with the elephant in the room. I think we have to drop the support for decorators. Some have been advocating this for years, others totally love decorators. Personally I hate to let decorators go. I think their DX and conciseness is still unparalleled. Personally, I am still actively engaged with TC-39 to still make decorators happen, but we are kinda back to square one, and new proposal will deviate (again) from the implementation we already have.

    Dropping decorators has a few advantages, in order of importance (imho)

    1. Become compatible with standard modern JavaScript Since fields have not been standardized with [[define]] over [[set]] semantics, all our decorator implementations (and the decorate utility) are immediately incompatible with code that is compiled according the standard. (Something TypeScript doesn't do, yet, by default, as for TS this is a breaking change as well, unrelated to decorators). See #2288 for more background
    2. MobX will work out of the box in most setups MobX doesn't work with common out-of-the-box setup in many tools. It doesn't work by default in create-react-app which is painful. Most online sandboxes do support decorators (MobX often being one of the few reasons), but it breaks occasionally. Eslint requires special setup. Etc. etc. Dropping decorators will significantly lower the entry barrier. A lower entry barrier means more adoption. More adoption means more community engagement and support, so I believe in the end everyone will win.
    3. Less way to do things currently it is possible to use MobX without decorators, but it is not the emphasized approached, and many aren't even aware of that possibility. Reducing the amount of different ways in which the same thing can be achieved simplifies documentation and removes cognitive burden.
    4. Reduce bundle size A significant amount of MobX is decorator chores; that is because we ship with basically three implementations of decorators (TypeScript, Babel, decorate). I expect to drop a few KB by simply removing them.
    5. Forward compatibility with decorators I expect it will (surprisingly) be easier to be compatible with decorators once they are officially standardized if there is no legacy implementations that need to be compatible as well. And if we can codemod it once, we can do that another time :)

    The good news is: Migrating a code base away from decorators is easy; the current test suite of MobX itself has been converted for 99% by a codemod, without changing any semantics (TODO: well, that has to be proven once the new API is finalized, but that is where I expect to end up). The codemod itself is pretty robust already!

    P.s. a quick Twitter poll shows that 2/3 would love to see a decorator free MobX (400+ votes)

    😱 2. Support proxy and non-proxy in the same version

    I'd love to have MobX 6 ship with both Proxy based and ES5 (for backward compatibility) implementations. I'm not entirely sure why we didn't combine that anymore in the past, but I think it should be possible to support both cases in the same codebase. In Immer we've done that as well, and I'm very happy with that setup. By forcing to opt-in on backward compatibility, we make sure that we don't increase the bundle size for those that don't need it.

    P.S. I still might find out why the above didn't work in the past in the near future :-P. But I'm positive, as our combined repo setup makes this easier than it was in the past, and I think it enables some cool features as well, such as detection of edge cases.

    For example we can warn in dev mode that people try to dynamically add properties to an object, and tell them that such patterns won't work in ES5 if they have opted-in into ES5 support.

    💪 3. Smaller bundle

    By dropping decorators, and making sure that tree-shaking can optimize the MobX bundle, and mangling our source aggressively, I think we can achieve a big gain in bundle size. With Immer we were able to halve the bundle size, and I hope to achieve the same here.

    To further decrease the build, I'd personally love to drop some features like spy, observe, intercept, etc. And probably a lot of our low-level hooks can be set up better as well, as proposed by @urugator.

    But I think that is a bridge too far as many already rely on these features (including Mobx-state-tree). Anyway I think it is good to avoid any further API changes beyond what is being changed in this proposal already. Which is more than enough for one major :). Beyond that, if goal 2) is achieved, it will be much easier to crank out new majors in the future :). That being said, If @urugator's proposal does fit nicely in the APIs proposed below, it might be a good idea to incorporate it.

    4. 🛂Enable strict mode by default

    The 'observed' one, that is.

    🍿API changes

    UPDATE 22-5-20: this issue so far reflected the old proposal where all fields are wrapped in instance values, that one hasn't become the solution for reasons explained in the comments below

    This is a rough overview of the new api, details can be found in the branch.

    To replace decorators, one will now need to 'decorate' in the constructor. Decorators can still be used, but they need to be opted into, and the documentation will default to the non-decorator version. Even when decorators are used, a constructor call to

    class Doubler {
      value = 1 
    
      get double () {
        return this.field * 2
      }
    
      increment() {
        this.value++
      }
    
      constructor() {
        makeObservable(this, {
          value: observable,
          double: computed,
          increment: action
        })
      }
    }
    
    • If decorators are used, only makeObservable(this) is needed, the type will be picked from the decorators
    • There will be an makeAutoObservable(this, exceptions?) that will default to observable for fields, computed for getters, action for functions

    Process

      1. [x] Agree on API
      1. [x] Implement code mod
      1. [x] Implement API changes
      1. [x] Try to merge v4 & v5
      1. [x] Try to minimize build
      1. [ ] Update docs (but keep old ones around)
      1. [x] Provide an alternative to keep using decorators, e.g. a dedicated babel transformation or move current implementation to separate package?
      1. [ ] Write migration guide
      1. [ ] Beta period?
      1. [ ] Create fresh egghead course

    Timeline

    Whatever. Isolation makes it easier to set time apart. But from time to time also makes it less interesting to work on these things as more relevant things are happening in the world

    CC: @fredyc @urugator @spion @Bnaya @xaviergonz

    🚧 experimental 💬 discuss 📖 documentation 🔨 breaking-change 
    opened by mweststrate 149
  • Road to 4.0

    Road to 4.0

    Hi all!

    Time to start thinking about the next major of MobX. These are some of the ideas I have around them. Global goal:

    Leverage modern features in evergreen browsers to address current quirks in the MobX api

    Quirks being:

    1. Observable arrays not being arrays,
    2. property additions not being observed
    3. Maps only supporting primitive keys and Sets not being present #800

    Tools to address the quirks: levering proxies #776 and native map implementations #800

    Breaking changes

    • observable(object) will now make all properties (including future ones) observable. To limit / specify the set of observable properties one will have to use extendObservable
    • Some utilities like isArrayLike will be deprecated
    • Array.move will be deprecated
    • Improve some typings #818, #817

    Also fix #940 if not done before

    Tasks:

    • [ ] Proxy arrays, kill faux array
    • [ ] Proxy maps
    • [ ] Proxy objects
    • [ ] Introduce sets
    • [ ] Update / finetune observable api
    • [ ] Verify performance
    • [ ] Document migration path
    • [ ] Re-evaluate existing api, remove deprecated stuff, deprecate things to be removed
    • [ ] Update docs, tutorials, gotcha pages
    • [ ] Double check if all issues referred to in this issue are adressed
    • [ ] Clearly communicate environment dependencies of 4.0, suggest 3.0 branch for projects with strict browser requirements
    • [ ] Clean up the legacy exports (for default and *)
    • [ ] Decompose extras namespace for better tree shaking
    • [ ] Remove spy / or spy events related to object mutations [discuss]
    opened by mweststrate 105
  • How integrate with Meteor Tracker example?

    How integrate with Meteor Tracker example?

    Hello @mweststrate.

    I'm trying to use/integrate mobservable with Meteor.

    Commented code is tested and working. This a sample of current proof of concept code:

    var afiliado = mobservable.observable(function() {
        var afiliado;
        Tracker.autorun(function() {
          // HARDCODE MongoID for test only
          afiliado = Orgz.collections.Afiliados.findOne({_id: 'EbPM2uWJhbd8rZP8P'});
        });
        return afiliado;
    });
    
    ViewPersona = mobservableReact.observer(React.createClass({
    /*
      mixins: [ReactMeteorData],
      getMeteorData() {
        Meteor.subscribe("afiliados", this.props.id);
        return {afiliado: Orgz.collections.Afiliados.findOne(this.props.id)};
      },
    */
      render() {
      //  return <ViewPersona_ afiliado={this.data.afiliado} />;
        return <ViewPersona_ afiliado={afiliado} />;
      }
    }));
    
    ViewPersona_ = React.createClass({
      render() {
        return <AfiliadoTabs afiliado={this.props.afiliado} />;
      }
    });
    

    "mobviously" I don´t get a plain object on afiliado var, just: ComputedObservable[[m#1] (current value:'undefined')]

    is possible do this ? what is the way?

    Thanks for your time and happy new year!!!

    🙏 help wanted 
    opened by bySabi 95
  • Multiple MobX instances in your application

    Multiple MobX instances in your application

    MobX has some internal global state to be able to track consumers of observables, schedule reactions etc. For this reason, the rule of thumb is that there should be only one instance of the MobX library in your application. Otherwise, if you have two libraries with their own dependency; reactions created in one library, will not react to observables created with the other library.

    Since version 4.2 you will be warned about this:

    There are multiple mobx instances active. See https://github.com/mobxjs/mobx/issues/1082 for details.
    

    Solutions to this warning are:

    1. If there should be only one mobx instance (libraries should cooperate & react to each other)

    Use peer dependencies

    If you intend the difference libraries you use to react properly to each other. In that case, those libraries should not declare mobx as a dependency, but as peer dependency instead. If you are bundling your libraries independently, make sure to mark mobx as an external dependency. The only place where mobx should be used as a direct dependency is in the "end product", which will provide a single mobx version to all libraries needing it. If you did that properly, the above warning will disappear

    Note that it is possible to use mobx.extras.shareGlobalState(), this will signal multiple mobx instances to share their global state (if the internal version differences aren't too big). This option mainly exists for legacy reasons; peer dependencies is the neater approach to this problem. shareGlobalState will be removed in 4.0

    2. If there are intentionally multiple mobx instances (libraries run in isolation)

    Use mobx.extra.runInIsolation

    In rare cases, you might be building a dynamic system that allows for third party plugins, or you are building such a plugin. In those cases you might be using mobx just for internal purposes inside the plugin, or in the hosting system. However, if you don't intend to use mobx as a communication layer between the plugin and plugin host (which is a very powerful mechanism btw!), you can suppress the above warning by calling mobx.extras.isolateGlobalState(). This signals mobx that it is the intention that multiple mobx instances are active in your application, without tracking each other, and will suppress the above warning.

    See #621, #1018, #1066, #1071

    3. More extensive trouble shouting guide

    See below

    opened by mweststrate 75
  • How to create a setter for an observable prop

    How to create a setter for an observable prop

    I noticed a repeated pattern in my code setup, usually in my store i would have some observables defined as follows:

    @observable loading: boolean = true;
    @observable name: string = '';
    // ..
    

    And then for each observable, i would create a matching setter action:

    @action setLoading = (value: boolean) => {
      this.loading = value;
    };
    
    @action setName = (value: string) => {
      this.name = value;
    };
    
    // ..
    

    Later on in my components that have the store injected in them, i can easily access this.props.store.name and this.props.store.setName('foo').

    This is not an issue when you have few observables, but for a large store with tens of observables, this becomes a tedious process and could lead to having errors when we wish to modify that pice of code, say the name of the observable for example. mobx should automate this for us i thought to my self, and indeed i found out about observable.box, although it requirers using .get() to retrieve the value of the observable, it would've been more convenient to have .get() automatically returned if .set() is not called.

    A perfect api would be something like this:

    loading: boolean = observable(true);
    // or with decorators (more convenient)
    @observable loading: boolean = true;
    // or if it cannot be implemented directly for some reason
    @observable @box loading: boolean = true;
    // or just @box
    @box loading: boolean = true;
    
    // usage
    console.log(this.props.store.loading); // prints true
    this.props.store.loading.set(false);
    console.log(this.props.store.loading); // prints false
    

    Can this be done currently with mobx? did i miss something in the docs? if not, any thoughts on implementing something like this?

    Coming back to react native, i've tried using boxed values:

    test: string = observable('foo');
    // also tried
    test: string = observable.box('foo');
    
    console.log(this.props.store.test);
    this.props.store.test.set('voila!');
    console.log(this.props.store.test);
    // also tried console.log(this.props.store.test.get());
    

    An error is being thrown in all cases: simulator screen shot feb 17 2017 2 05 18 am

    Please advise on this, thanks.

    opened by sonaye 70
  • [breaking change] Get rid of field initializers (and legacy decorators)

    [breaking change] Get rid of field initializers (and legacy decorators)

    Enabling useDefineForClassFields in TypeScript will prevent decorators from working (both transpiled and using mobx.decorate).

    This flag will be enabled by default for ESNext once class fields go stage 4: https://github.com/microsoft/TypeScript/issues/34787

    Possibly related to https://github.com/mobxjs/mobx/issues/1969

    Intended outcome:

    autorun using objectState is executed upon clicking on "increment" button. autorun using classState is executed upon clicking on "increment" button. autorun using classStateNoDecorator is executed upon clicking on "increment" button.

    Actual outcome:

    autorun using objectState is executed upon clicking on "increment" button. ⛔️ autorun using classState is NOT executed upon clicking on "increment" button. ⛔️ autorun using classStateNoDecorator is NOT executed upon clicking on "increment" button.

    How to reproduce the issue:

    https://codesandbox.io/s/fragrant-frog-x2487

    Versions

    TypeScript 3.7+ MobX - all tested versions (4.x, 5.x)

    🐛 bug 🔨 breaking-change 
    opened by Kukkimonsuta 65
  • Quest: A new name for Mobservable

    Quest: A new name for Mobservable

    After half a year I still think Mobservable is a silly name :). So let's find a catchy one for once and for all.

    Some suggestions so far:

    • Smurf (smurfs your stuff together. Smurf: Manage Ur Reactive Functions)
    • Microwave
    • TRP
    • Bricklin (spreadsheet inventor)
    • Deduce (everything from the state)
    • Dedux (from deduce. Although it sounds a bit too much like redux :-P)
    • Gumshoe (the detective that finds all relations in your data)

    Which one sounds most catchy / appealing?

    ... and feel free to add more suggestions :) But please check npm first, as most nouns and vowels are already taken.

    opened by mweststrate 64
  • Understanding MobX and when to use it.

    Understanding MobX and when to use it.

    Recently having used MobX, I'm trying to reason about why I'm really using it, and trying to truly understand the pros/cons vs redux and cycle.

    For redux, I think that it comes down to the fact that most people do not need the number one thing redux has to offer, extreme predicability and extreme testability, because their apps are not complex enough. Thus, when they're writing a bunch of reducers, dealing with extra verbosity, and having trouble grasping the new concepts, only to not reap the benefits, they feel like redux isn't all that useful.

    For cycle, I feel like the same way you've written in your docs, most people don't need the complexity and power that RxJS brings to the table over the more simple API MobX provides. MobX also lets you stick to the OOP style that most people are familiar with unlike Cycle, which heavily favors pure composable functions.

    Basically MobX lets you write your code as you normally would without forcing you to adopt and learn many new paradigms, and moreover, abstracts away the need to understand your view rendering logic. I think the real power of MobX is the fact that it's just easy.

    However, this also makes me wonder.

    Redux, ignoring its other limitations and its verbosity, will allow you to write an application that you are familiar with from top to bottom. If you put in the work, it'll be easy to get 100% coverage in tests, and to reason about piece by piece how your program flows.

    Cycle despite being more complex, following different paradigms, and needing you to understand RxJS ultimately seems more powerful than MobX if you grasp everything about it.

    Do you think the above is accurate?

    Also, where do you think MobX fits in when your application grows to be very complex? Like I said above MobX's appeal to me is that it provides similar results to the more complex libraries all while keeping it simple and easy to learn. But when should one pick MobX over Redux or Cycle when they're fully committed to learning and accommodating the complexities of either library? While MobX seems just as capable, the alternatives seem more advantageous if you invest the large amount time necessary to understand them. Is this accurate as well?

    An obligatory thank you for writing the library. I'm using it, and enjoying using it. This isn't a critique or anything, but just a deeper dive into understanding its place among other available tools.

    ❔ question 📖 documentation 🚶 stale 
    opened by AriaFallah 63
  • Can we improve the API in such a way that people will better understand what MobX does?

    Can we improve the API in such a way that people will better understand what MobX does?

    Can we improve the API in such a way that people will better understand what MobX does?

    The discussion was initiated in the 4.0 roadmap discussion here: https://github.com/mobxjs/mobx/issues/1076#issuecomment-359215082

    For an extensive background (discussing the current API design): https://github.com/mobxjs/mobx/issues/649

    The current api to create observables is generally well appreciated. However, currently I have one beef with it: It doesn't help people, especially not too experienced programmers, on what MobX does. Especially the conceptual difference between a value and a property is particulary confusing. I think this is partially caused by the fact that @observable anything generally just works, until it doesnt :)

    Some typical examples:

    1. @observable buf = new Buffer();. isObservable(this.buf) returns false. Which is correct. Should have been: isObservable(this, "buf") was intended.
    2. observable(thing) will make thing observable, unless it can't, in which case it will create box around the thing rather then enhancing thing.
    3. People arbitrarily use this.items = this.items.filter(...) or this.items.replace(this.items.filter(...)). With @observable items = [] both will work, but it would be nice if people grabbed the difference.
    opened by mweststrate 48
  • Idea: built-in support for promises

    Idea: built-in support for promises

    This proposal aims to making working with promises easier.

    Some ideas:

    1. Expose observable.promise(Promise) than returns { state, value, promise }. In other words, fromPromise of the mobx-utils package
    2. observable(Promise) and @observable x = Promise will automatically convert to a promise observable (e.g. call observable.promise) 3 Allow @computed to return Promises, and if so, convert this again in a promise structure.

    Example code:

    class User {
        @observable name
    
        @computed
        get profile() {
          // return a promise
          return fetch(`/endpoint/${name}`)
        }
    
        @computed
        get profileImageUrl() {
           // pluck the image from the profile promise
           return this.profile.state === "fullfilled"
              ? this.profile.value.image
              : "/defaultavatar.png"
        }
    
        @computed({ initialValue: [] })
        get tags() {
           // chain another promise onto the profile promise
           return this.profile.then(() =>
              fetch(`/endpoint/${name}/tags`)
           })
        }
    
        @computed
        get isLoading() {
           // are we fetching data?
           return this.profile.state === "pending" || this.tags.state === "pending"
        }
    
        @computed
        get isLoadingTakingLong() {
          // provide spinner if loading takes long!
          return Promise.race([
             new Promise(resolve => setTimeout(() => resolve(true), 1000),
             new Promise(resolve => this.isLoading.then((done) => { if (done) resolve(false) })
          ])
        }
    }
    
    const UserView = observer(({ user }) => (
       <div>
           <h1>{user.name}<h1/>
           { user.isLoadingTakingLong.value && <Spinner /> }
           <img src={user.profileImageUrl} />
           { user.tags.value.join(" - ") }
       </div>
    ))
    

    I checked, this will even work correct with debug tools like trace.

    Problems

    The problem with the decorators is that their return type signature is incorrect; as the type Promise<T> would be converted into Promise<T> & { state: "pending" | "fullfilled" | "error", value: T | undefined | error }

    Possible solutions:

    @computed
     get profile() {
          // upcast
          return fetch(`/endpoint/${name}`) as IObservablePromise
    }
    
    @computed
    get profile() {
          // wrapper fn (needing a better name)
          return observablify(`/endpoint/${name}`) 
    }
    
    // ... but still:
    @computed
     get profile() {
          // upcast
          const p fetch(`/endpoint/${name}`) as IObservablePromise
          p.state // undefined!
          return p
    }
    

    Caveat: only the first part of a computed will be tracked. But that is explainable and makes it somewhere easy as well.

    cc @spion @urugator @danielearwicker

    🍗 enhancement 🚧 experimental 
    opened by mweststrate 45
  • Next release

    Next release

    This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

    Releases

    [email protected]

    Patch Changes

    opened by github-actions[bot] 0
  • Add name to WHEN_TIMEOUT error if available

    Add name to WHEN_TIMEOUT error if available

    It's useful to have a name attached to the WHEN_TIMEOUT error for debugging/tracing. Since when already takes a name as an option this PR just adds the name to the WHEN_TIMEOUT error if it is available.

    Code change checklist

    • [X ] Added/updated unit tests
    • [X ] Updated /docs. For new functionality, at least API.md should be updated
    • [X ] Verified that there is no significant performance drop (yarn mobx test:performance)
    opened by evelant 1
  • Better React 18 support

    Better React 18 support

    mobx

    It now keeps track of a global state version, which updates with each mutation.

    mobx-react-lite

    It now uses useSyncExternalStore, which should get rid of tearing (you have to update mobx, otherwise it should behave as previously). Replaced reaction tracking utils with UniversalFinalizationRegistry. It works the same way, but I found it hard to orient myself in the original impl, so I rewrote it completely, hopefully for the better. It's also easier to reuse for class components.

    mobx-react (class component)

    Reactions of uncommited components are now correctly disposed. (fixes #3492) Reactions don't notify uncommited components, avoiding the warning. (fixes #3492) Removed symbol "polyfill" and replaced with actual Symbols. Removed this.render replacement detection + warning. this.render is no longer configurable/writable (possibly BC *). Reaction is no longer exposed as component[$mobx] (possibly BC *) Component instance is no longer exposed as component[$mobx]["reactComponent"] (possibly BC *) Deprecated disposeOnUnmount, it's not compatible with remounting. Refactored code. Fixed tests.

    (*) BC for non-idiomatic usage or when depending on low level (private?) API.

    I will update changeset once the changes settles.

    opened by urugator 2
  • Memory leak due to createAction function's closure

    Memory leak due to createAction function's closure

    There is a potential memory leak that happens in the res function, returned by createAction that preserves its closure. Due to this, number of closure keeping increasing as requests rise on the node server.

    Code reference: https://github.com/mobxjs/mobx/blob/e60b36c9c78ff9871be1bd324831343c279dd69f/packages/mobx/src/core/action.ts#L49

    Intended outcome: Memory leak should be avoided

    Actual outcome:

    image (14)

    How to reproduce the issue:

    1. Use mobx's action on server side
    2. Profile the node server
    3. Flood the server with requests

    Versions 6.3.2

    🐛 bug 
    opened by neeraj3029 2
  • mobx-react-lite can't find the dependencies of react-dom

    mobx-react-lite can't find the dependencies of react-dom

    I am using webpack Module Federation to shared mobx, but now webpack will prompt me that there is no react-dom in mobx- react-lite. image

    How to reproduce the issue: wenpack config:

      plugins: [
        new ModuleFederationPlugin({
          name: 'textDiff',
          library: { type: 'var', name: 'textDiff' },
          filename: 'remoteEntry.js',
          exposes: {
            '.': './src/index.tsx'
          },
          shared: {
            '@baidu/uiStore': {
                singleton: true,
            },
            react: { singleton: true },
            'react-dom': { singleton: true, import: false },
            'single-spa-react': {singleton: true},
            mobx: {singleton: true},
            'mobx-react': {singleton: true}
          },
        })
      ]
    

    package.json:

      "dependencies": {
        "antd": "4.20.0",
        "babel-loader": "^9.1.0",
        "css-loader": "^6.7.2",
        "html-webpack-plugin": "5.5.0",
        "less-loader": "^11.1.0",
        "mobx": "^6.7.0",
        "mobx-react": "^7.6.0",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-router-dom": "^5.3.0"
      },
    

    and I want to know why we didn't add react-dom to the dependency of mobx-react-lite image

    🐛 bug 
    opened by projectcss 0
Owner
MobX
Simple, scalable state management
MobX
The LMS (Life Management System) is a free tool for personal knowledge management and goal management based on Obsidian.md.

README Documentation | 中文帮助 The LMS (Life Management System) is a tool for personal knowledge management and goal management based on Obsidian.md. It

null 27 Dec 21, 2022
State management that tailored for react, it is simple, predictable, progressive and efficient.

English | 简体中文 ⚡️ State management that tailored for react, it is simple, predictable, progressive and efficient. ?? Introduction Concent is an amazin

cencentjs 1.1k Dec 28, 2022
A scalable, high-performance feature management and progressive experimentation platform

Introduction & Our Philosophy FeatBit is a scalable, high-performance Feature Management and Progressive Experimentation platform. Feature Management

null 345 Jan 1, 2023
Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic.

Synergies Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic. synergies is a tiny (

Lukas Bach 8 Nov 8, 2022
Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.

Recoil · Recoil is an experimental set of utilities for state management with React. Please see the website: https://recoiljs.org Installation The Rec

Facebook Experimental 18.2k Jan 8, 2023
🐻 Bear necessities for state management in React

A small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn't boilerplatey or opinionated, but still just eno

Poimandres 25.5k Jan 9, 2023
🏁 High performance subscription-based form state management for React

You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of React Final Form.

Final Form 7.2k Jan 7, 2023
🗃️ Centralized State Management for Vue.js.

Vuex ?? HEADS UP! You're currently looking at Vuex 3 branch. If you're looking for Vuex 4, please check out 4.0 branch. Vuex is a state management pat

vuejs 27.9k Dec 30, 2022
A state management library for React, heavily inspired by vuex

Vuex - But for React! ⚛ If you know vuex, you know it's as close as we get to a perfect state management library. What if we could do this in the reac

Dana Janoskova 103 Sep 8, 2022
Twitter-Clone-Nextjs - Twitter Clone Built With React JS, Next JS, Recoil for State Management and Firebase as Backend

Twitter Clone This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn de

Basudev 0 Feb 7, 2022
🔮 tiny robust state management

?? snapstate tiny robust state management ?? npm install @chasemoskal/snapstate ??️ watch for changes to properties ??️ track only the properties you

Chase Moskal 5 Dec 23, 2022
A tiny package for JavaScript Web App's state management based on RxJS & Immer

A tiny package for JavaScript Web App's state management based on RxJS & Immer

Xiao Junjiang 12 Oct 19, 2022
Tiny and powerful state management library.

BitAboutState Tiny and powerful React state management library. 100% Idiomatic React. Install npm install --save @bit-about/state Features 100% Idioma

null 53 Nov 5, 2022
Learning how to use redux - a state management library

Redux Learning how to use redux - a state management library What is Redux? Redux is a state management library for JS apps. It centralizes applicatio

Melvin Ng 3 Jul 18, 2022
🐻 Trying out the bear necessities for complex state management.

?? Zustand Demos My practice repository for the Zustand library--the bear necessities for complex state management. You can find some examples of how

Carlo Taleon 2 Jul 2, 2022
Blazing fast and lightweight state management framework 👓

StateX is a blazing fast and lightweight framework for managing state in a Javascript app. Features ?? Fast − Our APIs just run lightning fast, no mor

Truelines 7 Oct 8, 2022
Business class content management for Node.js (plugins, server cluster management, data-driven pages)

PencilBlue A full featured Node.js CMS and blogging platform (plugins, server cluster management, data-driven pages) First and foremost: If at any poi

PencilBlue, LLC. 1.6k Dec 30, 2022
Business class content management for Node.js (plugins, server cluster management, data-driven pages)

PencilBlue A full featured Node.js CMS and blogging platform (plugins, server cluster management, data-driven pages) First and foremost: If at any poi

PencilBlue, LLC. 1.6k Dec 30, 2022
The Frontend of Escobar's Inventory Management System, Employee Management System, Ordering System, and Income & Expense System

Usage Create an App # with npx $ npx create-nextron-app my-app --example with-javascript # with yarn $ yarn create nextron-app my-app --example with-

Viver Bungag 4 Jan 2, 2023
Simple shopping cart prototype which shows how React components and Redux can be used to build a friendly user experience with instant visual updates and scalable code in e-commerce applications.

This simple shopping cart prototype shows how React components and Redux can be used to build a friendly user experience with instant visual updates a

Ivan Kuznietsov 3 Feb 8, 2022