Observe changes in dynamically keyed objects with MobX and React

Mutating store values with MobX is great and easy. Set the observable value, your component re-renders, George Clooney is smiling and making coffee in the background. Life is good.

But when I try to change the values within a dynamically keyed Object, my component won’t re-render. George Clooney is punching his Nespresso machine and life kinda sucks.

Note: MobX5+ supports this out of the box, so if you can afford to leave support for Internet Explorer, for the love of George, stop reading and just use MobX 5+

Defining what we want to achieve

First, what do we actually want here? We want to add an observable property to our observable object, so that our component re-renders whenever we change that value, but we also want the component to re-render when the new observable property is added.

Because of that last bold part the extendObservable utility is kinda useless, because it just adds observable properties and tracks mutations on those properties. It doesn’t track whether the property is added or not.

Using Objects or Maps?

Unfortunately, you can’t just use your regular Object way of working for dynamically keyed collections, because Javascript doesn’t have a correct and efficient way to handle them from an observable standpoint.

MobX does provide you with a set of utilities (Object API) that make sure you can keep your current Object structure while working with Observables.

The alternative is using native Javascript Maps. Maps are kinda like objects on steroids and have a nice API. MobX even provides some extra methods for your Map.

Both are pretty similar in syntax and I’ll go through ‘m both.

1. Working with javascript Maps

Because working with Maps is a little different then working with objects, let’s create a little example that consists of three parts:

  1. MobX store with an Observable Map (this will replace our “object”)
  2. React component that renders the data from our Observable Map
  3. An onClick event in our React component that mutates the Observable Map

Before we move on: the full example

Don’t want to read stuff and get on with it? You can check out the full example on https://codesandbox.io/s/8y9j77xrp2

1.1 — Adding a Map to the store

I don’t want to dive too deep into the Maps, but if you’re confused by the pre-filled strawberries part and its syntax check out this part on adding data in the Map constructor.

1.2 — Render the items in our Observable Map()

What’s really awesome, is that we can spread our Map into an Array and use methods like Array.reduce or Array.map.

First we’re spreading the Map into an Array. Then we go the usual Array.map to create a nice Array filled with JSX. We can use some hipster Array destructuring to get our key’s and value’s from the Map.

1.3 — Actually mutating data

Let’s add an event handler that mutates the Observable Map but will still let the component re-render:

This event handler will first check if our observable Map filters has a key called ‘bananas’ and toggle (or add) its value accordingly using the get() and set() methods from the Map API.

Example

Check out a full example using Maps: https://codesandbox.io/s/8y9j77xrp2

2. Using the MobX Object API

The syntax is very similar to using Maps, except now you can use your regular familiar Object data structure. It’s important to emphasise that in order for this to succeed, you’ll have to get, set and iterate your data using the methods from the Object API. Read more about the MobX Object API here.

2.1 — Store

Now we’re just using a simple Object.

2.2 — Rendering data

Instead of spreading the data in the Map example, we’re using the entries utility from the MobX Object API to get the data. Of course, you could also use the keys or values utilities as described in the documentation.

2.3 — Mutating the data

Again, the code is almost identical to its Map counterpart. The only difference is that we use the get and set from MobX (Object API) instead of using the Map API.

Example

Screw these shitty code snippets. Check out a full example with the Object API: https://codesandbox.io/s/v3po3y7yry

That’s it

As you surely would expect, changes in your observable data structure now re-render the component, paramedics are nursing a calmed down George Clooney back to health and life is good.

Questions or feedback? Send me a message or DM on Twitter.