Adventures with React, Mobx, and Meteor

Tom Swales
6 min readDec 18, 2016

--

This weekend, I finally had a chance to experiment with the combination of React, Mobx, Meteor.

I have been working with React and Meteor for the past six or seven months (and Meteor plus its earlier Blaze library for just over a year), and enjoy both of these technologies tremendously, but have found the recommended interface between them (Meteor’s recommended createContainer approach https://guide.meteor.com/react.html#using-createContainer) to be frustrating to use, especially when it comes to passing in top-level state and rerunning subscriptions when state changes.

I have also wanted to progress from just manipulating a top-level React component state and passing down props, towards a more sophisticated state management solution, but despite having read the Redux manual twice, I still don’t find it intuitive. I recently came across the mobx library (https://github.com/mobxjs/mobx), and realised that this may be exactly what I have been looking for.

I have therefore documented my approach (using Meteor 1.4) for the benefit of anyone who is interested in the combination, and put my code on Github, in the hope of getting some feedback and further ideas about this combination of technologies.

Overview

As things stand with real-time Meteor-React web apps, there are three separate data loops that need to be managed, which are (in my terminology):

  • The Read-Write loop (reading from persistent data stores such as databases and APIs and writing to them). In Meteor, data is read from the MongoDB database using reactive queries, which react t0 new or changed data from the database. Secure method calls are used to make controlled changes to the stored data. The same can be achieved, although with less reactive magic, with REST APIs or other databases, allowing a variety of application architectures*.
  • The Publish-Subscribe loop, where the client subscribes to reactive data sources published by the server. In Meteor, these are established once, and then any new data on the server side is automatically passed to the subscribing client using a protocol called DDP.
  • The Render-Handle loop, where the client passes data down from a state ‘store’ to be rendered as UI components, and then handles events passed back by the components in response to user interactions. In React, data (and callbacks) are passed down as ‘props’, which cause lower-level components to re-render automatically when they change. Events can be passed back up to trigger changes in state, to initiate other loops, etc.

These loops are shown here:

Figure 1: Meteor-React data management loops

Achieving a zen-like flow of data within this system requires managing these loops in perfect harmony: exactly the right data, and no more, is passed through and between each of the cycles at exactly the right time, in response to user actions and inputs, or to database-driven data updates.

Architecture

So here is the architecture that I want to use to deliver this reactive data system:

Figure 2: Desired application architecture

On the client, Mobx acts as the state manager, passing down state to the React components, and interacting with Meteor through a ReactiveDataManager, which manages reactive subscriptions.

On the server, Meteor manages publications, account status, and methods and database / API calls and queries.

Mongo DB acts as the primary database, with collections defined for each of the entities that I want to keep track of.

Mobx as the ‘glue’ between Meteor and React

Both React (https://facebook.github.io/react/) and Meteor (https://guide.meteor.com/) are well-documented, and I have simply followed the recommended best practices in the documentation, so will not describe these in any further detail. However, I was unable to find a good description of how to connect up mobx to both React and Meteor, so I will document my approach here.

  1. Firstly, I create a mobx store (see mobx API guide here: https://mobxjs.github.io/mobx/refguide/api.html), which creates a number of reactive ‘observables’:

see code on Github

2. Now, I create a ReactiveDataManager object (just a class I made which implements a pattern) within the store, passing the store itself as a reference:

see code on Github

This does several things:

  • Uses mobx autorun (https://mobxjs.github.io/mobx/refguide/autorun.html) to listen to specific observables in the store (for example, an array of values to pass in as filters into a Meteor subscription).
  • When the observables change, the autorun creates a new Meteor subscription (stopping and replacing the previous one if it exists), with the new data / arguments retrieved from the store (using the mobx toJS() function (https://mobxjs.github.io/mobx/refguide/tojson.html) to convert it to a javascript data structure)
  • When the subscription is ready, it applies Meteor’s observe (https://docs.meteor.com/api/collections.html#Mongo-Cursor-observe) function on the cursor return by the .find() function. This observer has an ‘added’ and a ‘changed’ callback.
  • When either ‘added’ or ‘changed’ is triggered, the data manager fetches the new subscription data from the Meteor Minimongo client-side data store, and calls an update method (wrapped in a mobx action) in the store, and sets the new data. This will then be passed down automatically to React components.
  • Meteor observers will only stop when they are killed with .stop(), so I also ensure that when the autorun is triggered, the pre-existing observer is also stopped. I could also add an onStop() method to the Meteor subscription which would also stop the observer if the subscription is stopped by other means (not implemented).

3. I pass the store as an argument to the renderRoutes function (recommended in the Meteor Guide) which renders a React Router for the app. :

see code on Github

4. React Router then passes the state store reference down to App.jsx as an additional prop

see code on Github

5. The state can be accessed in App.jsx using this.props.route.state (ugly but it works!).

see code on Github

I found this a much easier way of passing state plus reactive data down to components than with Meteor’s createContainer.

In App.jsx, the component is declared as a mobx observer using the mobx-react npm package, which uses this format:

const App = observer(class App extends React.Component {
// code
});
export default App;

The beautiful thing about this is that App.jsx can now access the store state just by using its observables, and will receive new data instantly! It can also pass the state further on down the component heirarchy, where child components can also be observers.

6. The icing on the cake is that mobx can be used for a lot more

For example, I can create a component (Example.jsx) which renders multiple child components (Dependent.jsx). I want to keep some state about whether the Dependents are visible or not. I could keep this state in Example’s local state (but this is ugly). I could somehow mutate the data about the Example which was used to render the component (but this is also ugly). However, Mobx allows me to create a map in the state store, using map(), where I can just put the _id of the Example, and then toggle whether or not it is visible.

showDependentsMap: map(),                                   toggleShowDependents: action((exampleId) => {
if(this.showDependentsMap.get(exampleId)){ this.showDependentsMap.set(exampleId,false); }
else { this.showDependentsMap.set(exampleId,true); }
}),

I then use .get(_id) directly (and reactively) in the Example.jsx render method to decide whether to show the dependents or not. This to me seems to be a very clean way to separate UI state from UI data.

Conclusion

I found mobx to be a great way to connect Meteor and React together. It allows very fine-grained control over exactly when subscriptions are re-run, and responds to any changes in the subscription data by instantly passing on the data directly to React presentational components

Mobx also provides a lot more functionality besides, which I haven’t even got round to fully exploring yet.

I very much hope that this commentary and code example will be useful to you and look forward to feedback. There may of course be some issues to do with efficiency that I haven’t identified yet. Please feel free to comment below.

  • Interested readers should take note of the new GraphQL-based Apollo project http://dev.apollodata.com/ from the Meteor Development Group, which will radically change how this loop works.

Update: for a look at using Mobx, React and Meteor together in a working app, check out my other article: https://medium.com/@swalta/journal-of-a-bot-wrangler-88af8c48eab

--

--