Mobdux: Combining the good parts of MobX and Redux

Redux

The Redux Flow
  1. Very explicit
  2. No magic library, you write vanilla javascript adhering to the redux pattern
  3. Dumb components have smart wrappers that take minimal props (usually an identifier or nothing) and look up all the props needed by the dumb component.
  1. Boilerplate (making a counter increment involves writing: a selector, an action-type an action-creator and a reducer)
  2. Every state change reconciles every smart component (i.e at the very least every smart component will have its mapStateToProps function re-run)
  3. Selectors are an optimisation, not a first class citizen

MobX

The MobX Flow
  1. Simple and concise, minimal boilerplate necessary.
  2. Minimal re-renders out of the box (observers are smart enough to know when they should re-render themselves based on the props they use).
  3. Works really well with typescript as MobX is written in typescript so there’s no need to rely on third party type definitions.
  1. Relies on the magic of observables and observers.
  2. Leaks knowledge of MobX into components through observer decorators. All components become coupled to MobX by taking observables or stores as props.
  3. No dumb/smart component separation; props are often broken up and passed down the component hierarchy.
  1. Easier to move things around in your component hierarchy.
  2. No pass-through props, where a component forwards props to its children despite not needing them for its own rendering.
  3. Easier to test / re-use / write storybook stories for the dumb components that are only interested in rendering.
  4. Dumb components are completely decoupled from Redux, allowing for re-usability if the consumer decides to change state frameworks (to MobX or relay for example).

Inject

It turns out MobX offers an inject method that allows it to be used in a way that’s similar to Redux’s connect method, very briefly mentioned in the mobx-react documentation:

‘customising inject’ example from the mobx-react readme

Using inject to separate smart and dumb components

By combining a single MobX store with heavy use of the inject method, we end up with the following architecture:

The combined Redux/MobX flow
  1. Keeps the simplicity and conciseness of MobX.
  2. Minimal re-renders out of the box.
  3. Maintains the smart/dumb component separation.
  4. No need for the observer decorator.
  5. No need for memoized selectors (just a mapping function per component).
  6. Smart components only reconcile when they should (not on every state change).
  1. Still relies on the MobX magic.
  2. Referencing properties nested within a computed property in a mapping function can cause unnecessary reconciliation (and re-renders).
  3. Directly returning an observable array in a mapping function will prevent the component from reconciling (or re-rendering) when the array changes.
  4. We want a normalised map of entity data so we can easily pass ids around for our smart components and actions, but MobX prefers to work with denormalised data.

Using mobdux

Here’s a brief example of a mobdux application that maintains a list of independent counters. Note the complete lack of boilerplate, and the minimal passing of props!

A simple counter-list application

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store