React and Immutability: a bad combination

Jasim A Basheer
3 min readNov 4, 2016

--

React’s reliance on immutability is not a feature, it is a bug.

Take a look at its programming model: it simply re-renders all views every time the state changes. We can do the same in naive Javascript with a render function that gets called often. But recreating the DOM all the time is very inefficient. React fixes this with a Virtual DOM that only patches the DOM with any changes, keeping performance under control. When you’re writing code at this level, you don’t need to worry at all about immutability. Just mutate something, and call setState({}) to force a re-render.

But what if you have a large array of data to render and performance becomes a concern? This is where immutability comes into play. React improves its performance by re-rendering only components that have actually changed. Otherwise they don’t even hit the VDOM.

This is how it is done: as you iterate over the array that you want to render, you do a dirty check. Re-render it only if it has changed from the last time it was rendered. React gives its users a way to define this dirty check as the lifecycle method shouldComponentUpdate. This callback passes us a reference to the old object and a reference to the new one, so that we can tell it to either re-render, or happily skip.

This comparison is viable only if it is a shallow reference check. A deep equality is out of question — we’re here in the first place to avoid work, and comparing the entire object defeats the purpose.

And that is how React got its love for Immutability. But its cost is paid by its users — we have to be disciplined about reference hygiene and diligently do shallowClonefor all state mutation. If something changes, it must get a new reference. This also makes changing nested objects difficult, causing the need for shenanigans like React.addons.update and an even more invasive approach — boxed data-structures like ImmutableJS.

We wouldn’t have had this problem if Javascript was a language with immutable values. But there is a reason why people write mutating code. It is easy. And user interfaces and immutability doesn’t go very well together. A GUI is a very direct layer on top of a large state tree, whose nodes keep changing at all levels of the hierarchy. There is no getting around that. It would be so nice if we could just say item.details.price = newPrice and be done with it.

But what do we have instead? We have Flux. Now to do even trivial things, we have to sign in triplicates and dance to Vogon poems. In technical terms, we first action-create an action, fire it into the void and hope it gets intercepted by a dispatcher, who will do a string comparison on the event, and if satisfied, calls something in a store/reducer, which at last mutates the state. Passing mention of providers and middlewares and thunks and sagas, and one can only hope there is some method in this madness.

I think this is the wrong direction to take front-end application development to, and am happy to see a library like MobX gaining adoption. It has a risk of magic, but it could be, like React, well-contained magic that doesn’t leak. I’m working on a few things to test this, and might have something to say about it in the future.

Thanks for reading. Comments welcome.

--

--