AMA with MobX

AMA with

MobX

An up and coming state management solution that makes state management simple and scalable

Held on

The Object.Observe spec was pulled from ECMAScript. Many people praised React/Redux as an escape from the Observable model. So generally, when people see "Observables", they tend to stray away, if not simply for the appearance of them falling out of popularity. How are MobX's Observables different? Why bring the observable model into the React ecosystem?

Observables are a very broad concept, and depending on the context they have a good or a bad name. Both the ES7 Object.observe and RxJS provide (different kind of) observables. Personally I'm happy that the ES7 proposal didn't make it, as the semantics where confusing at least. In MobX observables are really an implementation detail. In hindsight the @state might have been a better name for the decorator than @observable. Observability is a concept that is used internally by MobX to detect when stuff that can be derived from state is not consistent with the state itself anymore. When using MobX you shouldn't be bother anymore with wiring up and tearing down observable listeners. MobX abstracts away from exactly that. But since observables are used internally, MobX can establish very fine grained observers so that it can determine very precisely which derivations are out of sync. But in principle MobX is about reactivity, observability is just the means to get there, not a goal in itself.

An interesting difference with the ES7 proposal is that MobX observers are always triggered synchronously. This simplifies the semantics a lot and makes tracing and debugging easy; if you want to know why a derivation runs, just set a breakpoint and the stack will contain the change that triggered it. In the upcoming version the DevTools will even assist you in doing this.

fibric

Denny Trebbin

1y · node.js member. hacking web & mobile. love pixel perfect design. hate quick & dirty. conjuring in germany. kid of the 80s.

Why is MobX called MobX, how did you come up with that name?​

Initially the project was just an experiment with no real intent to open source it. So my 30-seconds name for the repo become Mobservable (contraction of mob and observable; a lot of observables). When I OSS-ed it, i figured that Mobservable wasn't really about an api for observing data, but about reacting to data. So I started looking for a new name and ended up with MobX: short, still relates to Mobservable, and the X hints to reactivity. The "Mob" can also refer to the mafia, which nicely conflicts with state management :-), and might be fun for a logo (which I never got to, so suggestions are welcome ;-)).

Thanks! Okay then, I will create an issue about the logo. I have an idea. Do you like cars from 1930? ;-)

Sounds cool!

michaelgilley

Michael Gilley

1y · Frontend Engineer at Zapier

One of the core principles of Redux is that the state is read-only so that any updates to the state must occur in a simple function (reducer) that replaces the state rather than mutating it. This gives the organizational benefit of keeping all state management code in a concentrated place. Has this caused any drawbacks for understanding where and when the state might be updated since MobX allows for scattered mutations throughout an app? Is testing more difficult especially in complex applications that might be drawing data from multiple sources asynchronously? Also, would this account for extra ramp-up time for new members to the team? Thanks!

Ha, every sentence a question :)

I think the freedom in organizing and managing state is both the strongest and weakest point of MobX.

Strongest because you can think about the structure and logic of your domain state completely independent of the rest of your app, and I believe you can achieve better decoupling of your state from the UI then with for example Redux.

It's also a weak point because MobX doesn't do anything to prevent your state structure to become a mess or scattering logic all over the place. I do believe domain logic should not be in components but MobX doesn't enforce it. Personally I work a lot with controller classes or model classes with instance methods.

MobX 2.2 partially addresses the mutations-from-anywhere issue by introducing actions: functions need to be decorated as actions before they are allowed to modify the state, so that you can clearly recognize them, and not modify state anywhere else.

Funny enough, I didn't hear anybody complain about not being able to trace where mutations came from. The simple reason for that is that derivations in MobX are always run synchronously (unlike similar frameworks!). So even if a derivation is triggered unexpectedly, you simply can find the causing mutation back in your stack.

Most people coming from Redux find working with asynchronous data is ridiculous easy in MobX. Testing asynchronous stuff is not significantly harder or easier then it is without MobX. You can for example use when(observable expression, effect) which is quite similar to a promise.

I think MobX is a lot easier to learn for new members than most other state management solutions. For two reasons, it allows you to use concepts developers many are familiar with, like references and classes. And secondly with MobX you can focus on a the logic around a small piece of domain model, without needing to know how it ties to the rest of the application, as the app should largely react to state changes automatically.

Thank you for the patience with all the questions. :) Would you mind going into a little further detail as to how you tie these controllers/classes to components since both, presumably, would depend on your MobX store?

I'd also like to hear more on how MobX is better at decoupling the state from UI.

Thanks!

The decoupling is quite easy to explain: in Redux you need to connect to the relevant parts of your state. The more your state tree differs from the component tree, the more often you have to do this. If you want to get the best performance possible, you should even adjust your state tree to exactly your components needs (see this twitter thread and test repo. This means that if you organize your UI differently, your state has to be adjusted.

In a lesser form this also applies to reducers. Where an action in mobx that updates just a todo only needs about the todo (e.g. toggleTodo(todo) => { todo.completed = !todo.completed }), the reducers also needs how the todo relates to the rest of the state tree in order to reproduce a new state, so you get something like:

toggleTodoActionImplementor(state, todoId) {
  return {
     ...state,
     todoId: {
        ...state[todoId],
        completed: state[todoItem]
    }
  }
}

(some libraries make this easier of course)

So changing the state tree has a larger impact here (for example when changing from a todo map to an array or vice versa, the toggle action is affected by this!)


Controllers (basically a collection of action methods) in general don't relate to MobX. Like the above toggleTodo does nothing MobX specific. It would work with MobX observables and plain JS objects equally fine. The only MobX specific thing would be wrapping it in the future action decorator.

Controllers might use autorun to manage some side effects automatically, like sending data to the server, but you can also keep doing this 'by hand', explicitly.

Models do relate to MobX, they have a bunch of @observable and @computeds around. But again you can design them without MobX in mind and add the decorators as afterthough when you build the UI for example.

So because MobX is less opinionated and more flexible in how you structure your state, it is easier to decouple. Things like @observer work with all observables, regardless how you got them into your component. So if your component gets it's data from the context or even from a singleton in the closure, it will work anywhere in your component tree, without needing to change other parts of your application or restructuring the state tree.

Does that help?

dortzur

Dor Tzur

1y · Full Stack JavaScript Developer

If you get hit by a bus, or lose interest in the project, will there be anyone who's active enough to keep the project going?

There are a few people in the open source community that are familiar with the code base and have contributed bug fixes etc.

Besides that MobX plays an crucial role in the work of 3 development teams at Mendix. So although new features are mainly developed for the community (for example at Mendix we don't use babel transpilation or react native), Mendix will keep at least the lights on if I would be hit by a bus :).

Are there any particular types of applications you would avoid writing using MobX?

Just curious about potential weak points as no solution is perfect. :)

Hard question to answer, as so far I am quite amazed in which kind of applications it has been used for which I did never considered it, like webGL / canvas based stuff and such. But I think MobX shines when your domain model is highly interwoven with lot of CRUD like operations.

It makes less sense to MobX with systems that have a strong focus on event based, append-only mechanisms. This kind of applications are better suited by an immutability based approach. But I think that is a minority of the apps, most applications are about data entry and modification.

What are MobX performance characteristics ? How bad does it degrade (if ever) as the number of observers increases, say, to a few hundreds ?

MobX started for two reasons: 1) existing state management solutions weren't fast enough 2) we wanted to write simple, straight forward code. Imho the project succeeded in both goals.

I touched the subject of performance shortly on the React Amsterdam conference, see this slide (video will appear soon) or this tweet with stats that compares performance with Redux.

In short: MobX very efficient; the number of observers is not influenced by the number of derivations, but by the number of active reactions (like, amount of visible components). Derivations will tend to go asleep if not used by some reaction. Fine grained observability allows for much more optimizations that coarse grained immutable state or manual subscriptions. Since in MobX subscriptions are under the hood, observers are not established by using callbacks, it is just a simple object pointer in an array. These things make it all very efficient.

Thanks Michel :)

How do you feel about ES6 proxies? Currently MobX uses getters/setters, but proxies could provide cleaner way of intercepting changes. Do you think in distant future MobX could in some major version start using proxies instead of getters/setters?

I hope that ES6 proxies will soon become mainstream (it is not yet available in Safari and some IE versions). A lot of code of MobX could then be thrown away. Making arrays observable was a very complicated task, and stills has limitations (like observable arrays not being real javascript arrays), which as it seems, could all be solved by ES6 proxies. So I'm eager to adopt it. Sadly it cannot be polyfilled, but yes, in a distant future I definitely would like to move to proxies.

ddunderfelt

Daniel Dunderfelt

1y · Developer

What sparked the initial revelation that "hey, if I could simply OBSERVE changes of this object..."?

When we started a green field project at Mendix, almost a year ago, i started with the question: how should the code look that we want to write. Because we knew our problem domain particularly well; hundreds of different domain object to be edited by hundreds of different editors. Knowing that we would have a lot of back and forth on the components. So we wrote pseudo code like:

class ProfileView {
   render() {
      return <span>{this.user.displayName} ({this.user.organization.name})</span>
   }
}

We didn't want to be bothered how things like displayName or even deep references like organization.name and would be kept in sync with the state. So from there on I started thinking how we could achieve the above. We figured that components need to detect themselves what data they use to be able to subscribe to it. That leaded to the whole observe and MobX thing. I think in the end we came pretty close to the above pseudo code :)

sandeep

Sandeep Panda

1y · Co-Founder, Hashnode

What is your preferred editor for writing React based apps?

Visual Studio Code. I am a heavy typescript and (T/J)SX user, but I think it is an awesome IDE for normal javascript as well.

MobX's perf and simplicity are very appealing. But - Views mutating state are a concern. Is there any way to prevent it? Either runtime or static analysis (eslint)

mobx-react already forbids currently to change the state inside the render function of a component. This is done internally through the function mobx.extras.allowStateChanges(false, block).

In the next minor version of MobX, 2.2, a new feature will be added: the action function / decorator can be used to wrap around all your actions. If you then run MobX in strict mode, it will throw on any attempt to mutate state outside of actions.

n4ndan

Nandan N

1y · Compassionate Coder

How long have you been using MobX in your current company?

Development started almost a year ago. It has been used since then at Mendix and we use it in a 40Kloc production system since roughly October as the central mechanism to manage state

nate_russ

Nate Russ

1y · Teen Coder / Adventurer

How much time do you spend working on MobX everyday? Is there a team (inside Mendix) working on this?

I travel each day by train to the office, so basically I work on MobX in the train (less then an hour per day) and also a bit during the evenings. But that differs a lot depending on how many other activities I have :)

At Mendix some teams are depending on MobX, but there is no team behind it. I do have several days a month to work on it though.

Are there any companies/projects apart from Mendix that are using MobX in production?

Yes, I'm aware of at least ~5 companies that use MobX in production for complex projects as they contacted me about it in one way or the other. There are even some famous companies in there.

In apps with immutable state it's easy to implement undo by just switching to previous states. Would making state snapshots work at all in MobX somehow? Otherwise what would be a good way to implement undo functionality? Just preserve command list and apply/unapply them?

Efficiently snap-shotting state is possible and not too hard in MobX, as demonstrated in the demo of my Reactive2015 talk. A future version of MobX (or the devtools) will probably even have a standardized approach to this.

If you start to build undo/redo you will soon discover that undo/redo is often a lot more than just 'snapshotting'. Two other aspects play an important role in undo/redo: transactions (actions of a user might be consist of multiple smaller actions) and side effects (you don't want to undo just the client state, but also revert data that was already sent to the server).

So for that reason I would use snap-shotting as debug utility, but command apply / unapply if you need complex undo / redo patterns.

fibric

Denny Trebbin

1y · node.js member. hacking web & mobile. love pixel perfect design. hate quick & dirty. conjuring in germany. kid of the 80s.

I struggle with React-Router and its nested routes concept. That's why I put visibility logic of dump components into smart components. URLs can contain some app state too. To me, it's difficult to maintain state in Routes and ​Stores. On MobX docu router is only mentioned but never shown.

What do you think about UI Routing? Is it time to move on and keep all the state where it belongs to, in a state machine/state manager like MobX/Redux?

For routing I try to resolve it somewhere in the root of the component tree, and just use it to update the view state of the application (like what is the current selection, or even page, etc). Then the rest of your application can become router unaware.

For updating the current url I always do one of the following:

  1. just update it as part of your action, completely unrelated to mobx (for example when hitting a "login" button or something like that).
  2. do it as side effect of a state change in an autorun, for example if the selection changes, see for example: https://github.com/mweststrate/mobx-contacts-list/blob/master/src/app/stores/view-state.js#L31

No router. Great! :-)

yeah that was an experiment. actually routers don't do that much it seems in modern browsers :-D

jeremwri

Jeremy Wright

1y · Solving real world problems with technology

How does MobX compare to Redux? What real world problems does MobX try to solve (that couldn't be solved by other solutions)?

See also this answer

At Mendix we have a really complicated model (500+ classes, thousands of attributes, lots of inter data relationships). We have partners that write plugins that modify, analyze or generates these models. Independently we have partners that write visualizations. So we needed a system that was

1) simple to write (partners are often no full time JS devs) 2) highly performant 3) ensures consistency without needing to state data needs / force UI updates 4) CRUD actions and relations are very easy in MobX (no normalization needs etc)

I think the benefit of using MobX is that you have to learn way less concepts and it keeps your code more straight-forward. Since it is unopinionated about how you structure your data, it is more suitable to highly decouple state from components. In Redux your component structure and state structure are highly related each other, especially when performance matters. In MobX this is a lot less the case.

Thanks for the reply!

Is there any way to hookup to property which is not on the observable yet?

Something like this:

        const store = observable({test1: null});
        autorun(() =&gt; {
            console.log(store.test2);    
        });  
        extendObservable(store, { test2: &quot;aaa&quot; });

Yes, you can use mobx.map for this. With mobx.has you can observe not-yet existing properties

const store = map({test1: null});
autorun(() => {
  if (store.has("test2")
    console.log(store.test2);    
});
store.merge({"test2": "aaa"});

EDIT Object changes (including adding observables) can also be detected by using

const store = observable({test1: null});
mobx.observe(store, (change) => {
  if (change.type === "add")
    console.log("attribute " + change.name + " was added!")
});
extendObservable(store, { test2: "aaa" });
jonathanS

Jonathan

1y · Breaking things by touching

What tech stack do you work with? Isomorphic apps or Universal apps - which term do you prefer and why?

Node / Typescript / Webpack / React / Mongo / Postgresql / Xtend

I prefer SSR (Server Side Rendering). It is unambiguous and to the point :)

You mentioned MobX 2.2 action decorator. Do you plan to have any method that will enable to "implement" actions outside of obervable as well?

I created an experimental model/wrapper around mobx and I am curious if I could use that feature as well (https://github.com/pvasek/mobx-app-model).

Interesting! Yes that should be no issue, action is just a higher order function that invokes and returns the value from the the wrapped function without signature changes.

nate_russ

Nate Russ

1y · Teen Coder / Adventurer

Nice work Michel! If someone wants to contribute to MobX project, how should they go about it?

  1. Play with it
  2. Improve docs based on your experience / file feature requests
  3. For minor improvements just create a PR, for bigger improvements discuss them in an issue first
  4. Feel free to pick any open issues (make sure to mention that)
  5. So far I have been blown away with a few PR's already that added really important improvements, like the dev-tools are largely the work of @Andy_Kogut
jeremwri

Jeremy Wright

1y · Solving real world problems with technology

  • What tools do you recommend for writing unit tests for React components?
  • How do you manage style in React?
  • Do you like Cats or Dogs?
  • Enzyme and tape / tape-runner (and faucet 😎)
  • css modules / sass (but I think JSS is very interesting as well)
  • cats :) but currently we don't have any as he/she would quickly become a toy for our toddlers
bill_89

Bill Schrockwitz

1y · JS is the knife that I carry!

I have the following questions :

  • What types of apps should use MobX for state management?
  • What types of apps shouldn't use MobX?

Thanks for this AMA. :)

You're welcome :)

I think the following answer applies to this question as well: https://hashnode.com/ama/with-mobx-cinspo7i500vyxs53buh8ebls#cio08nna601dunt530tr5xu3e

Connect with MobX and other awesome developers

Join this AMA and connect with developers

loading ...