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?
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.
Both are pretty similar in syntax and I’ll go through ‘m both.
Because working with Maps is a little different then working with objects, let’s create a little example that consists of three parts:
- MobX store with an Observable Map (this will replace our “object”)
- React component that renders the data from our Observable Map
- 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
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
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
set() methods from the Map API.
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
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
set from MobX (Object API) instead of using the Map API.
Screw these shitty code snippets. Check out a full example with the Object API: https://codesandbox.io/s/v3po3y7yry
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.