Pulling State out of React

Photo by Clément H on Unsplash

A pretty common pattern in React is to use container and presenter components. Dan Abramov talks about it here, but the gist of it is that you want to separate how the component works from how it looks. To do this, you create two components, a presenter and a container. The presenter is typically a simple stateless functional component, and the container contains all the lifecycle methods and state that determine how the component will behave.

Typically, the containers render function looks something like this:

render() {
return <WrappedComponent {...this.props} {...this.state} />

Basically, it does very little. The point of the container isn’t to render anything. In fact, it shouldn’t even know about rendering at all. Ultimately, our business logic has nothing to do with React. All containers care about is that the right things happen when the user asks, not how the user asks. It would be great if our container could just be a plain Javascript object.

Let’s see if we can make it work with a simple classic example, a counter:

We have a basic presenter:

And a javascript object that represents a counter:

Let’s make a higher-order component which takes a store and a component and returns a new component with the relevant parts of the store being passed in as props:

And hook it all together:

This example exists in the V1 directory in this code sandbox.

Ok, so our state gets into the presenter (the counter is set to 0), but pretty quickly you’ll find out that the buttons don’t work. Technically they do increment and decrement the counter, but the presenter isn’t re-rendering so it looks like nothing is happening. Let’s make it so our store gets a callback that allows it say when its state has changed, allowing anyone who cares about the new state to have an opportunity to grab it. We could write it like this:

So now the store can tell us when things have changed, we need to get our component to re-render when that happens. And how do you make a component re-render in React? You call setState. Let’s update our createCounterContainer function to allow the store to tell us when to re-render:

And connecting these together:

This example exists in the V2 directory in this code sandbox.

Things seem to be working! We can now use a plain javascript object to represent the state and logic of our app. Unfortunately, our createCounterContainer function is specific to our counter store because it has to know what props to pass through. It sucks for us to have to rewrite this container function every time, so what if we make it more general.

Now we take a second argument to our createContainer function which tells us how to turn our store into the props that our presenter expects. In this case, this function is pretty simple:

And finally, putting them together:

This example exists in the V3 directory in this code sandbox.

Now we have a reusable function for creating container components! It’s really nice that we can write our store without it having to know anything about React, but it still sucks that the store has to manually tell you when things change. That could definitely result in some pretty annoying bugs. It would be great if there was a way that our app could just magically know when state has changed…

Guess what? There’s a library for that! Mobx (and mobx-react).

Mobx efficiently keeps track of your state and can seamlessly handle the notifying of updates. Even better, it does it optimally, only triggering re-renders when they are strictly necessary. What does our store look like with mobx?

Pretty much exactly what we started with originally, but we let it know that the value is observable. Then we make sure our container component is marked as an observer, allowing us to remove the update triggering logic:

Finally, using our latest createContainer function:

And that’s it, nothing else changes. This final example exists in the mobx directory in this code sandbox.

Now the business logic is isolated into plain javascript objects and we don’t even have to think about how or when rendering happens. This gives us a couple of big advantages:

  1. The stores are easy to understand because there is no react boilerplate.
  2. Testing is easier because you don’t have to worry about rendering that isn’t actually relevant to the tests.

Check out a live demo of these 4 versions of the code here and let me know what you think of this approach: