Redux: Why cant state be mutated....

Created on 20 Sep 2015  ·  13Comments  ·  Source: reduxjs/redux

Hi,

I read with interest about immutable, and how you should not mutate your state. I understand to some degree why that is, yet, the "concerned about the time spent creating new objects" side of me is either not understanding in JS how fast/slow this is, or I am missing something else.

Typically, creating a new object, more or less cloning an old object to the new plus the new state additions (or subtractions) seems to me like it would be more time consuming then just modifying the existing state. So I am trying to grasp why the immutable thing is so important and does it affect something else down the chain that I am just not seeing?

Furthermore.. I would think that a lot of what Reduct offers could be added to the react-core, and maybe even take advantage of its DOM diffing engine... maybe even to help determine if state should mutate or not. I am not sure if the process of mutating state would be faster or slower than diffing the state with the new state and change (mutate) only the parts of the actual state object that should change?

I am relatively new to all this so forgive me if I am missing something around the notion of not mutating state. I do love the idea that components should only render immutable data... but then again, to the component, it doesnt care or know if its immutable or not.. its just data that gets passed in from somewhere, so I am struggling a bit with the whole immutable vs mutable state.

question

Most helpful comment

Just to clarify: state is not deeply cloned on every action. Only the parts that changed are cloned (again, not deeply—depends on what changed). For example, when a todo is edited in TodoMVC app, only that todo object is cloned. The rest of the todo objects are the same. Of course, a root new todo list array is created, pointing to the new object, but the objects themselves are not cloned if they have not changed. Therefore it's not as expensive as it may seem. Furthermore, when it gets expensive (e.g. fast array changes), you can start using a library like Immutable.js that has very fast copying thanks to structural sharing. With Immutable.js, copying even large arrays isn't really that expensive because large chunks of the memory are reused. Finally, whether with or without Immutable.js, immutability helps us efficiently rerender the app because we know _what exactly_ has changed thanks to the objects not being mutated.

All 13 comments

It's all about predictability and reliability.

Reducers in redux are pure functions, which means they have no side effects. As soon as you start looking at some external state to those functions, they are no longer pure. And if they're not pure, they can be unreliable. And that causes bugs, many of which can be _very_ hard to track down.

I've found through writing pure functions, I produce less errors in my code and I am more productive. And hot module reloading (enabled by using pure functions) is a turbocharger on top of that productivity.

And you receive for free features like time travel which is very handy.

Closing as duplicate of #328.

Just to clarify: state is not deeply cloned on every action. Only the parts that changed are cloned (again, not deeply—depends on what changed). For example, when a todo is edited in TodoMVC app, only that todo object is cloned. The rest of the todo objects are the same. Of course, a root new todo list array is created, pointing to the new object, but the objects themselves are not cloned if they have not changed. Therefore it's not as expensive as it may seem. Furthermore, when it gets expensive (e.g. fast array changes), you can start using a library like Immutable.js that has very fast copying thanks to structural sharing. With Immutable.js, copying even large arrays isn't really that expensive because large chunks of the memory are reused. Finally, whether with or without Immutable.js, immutability helps us efficiently rerender the app because we know _what exactly_ has changed thanks to the objects not being mutated.

Ahh.... see that last bit makes sense now... I assume by this you are saying that because of the component render change due to only what state changed, the react diff/redraw engine is faster as a result? Similar I guess to shouldComponentUpdate

I assume by this you are saying that because of the component render change due to only what state changed, the react diff/redraw engine is faster as a result? Similar I guess to shouldComponentUpdate

Exactly, react-redux uses an aggressive shouldComponentUpdate under the hood thanks to the immutability guarantee.

@gaearon Sorry to necro this one a little! but your comment about fast array updates:

Furthermore, when it gets expensive (e.g. fast array changes), you can start using a library like Immutable.js that has very fast copying thanks to structural sharing.

I'm looking at using redux for handling the updates to an array going up to 8000 positions and expect the throughput to be in the region of 10-100 changes per 100ms. Am I fighting a losing battle to try to manage the state that way using redux?
We're planning to represent the state through a canvas rather than the DOM, but I'd like to understand whether in your experience the frequency of updates would cause us a problem or not.

@dougajmcdonald : My first question is, can any of that work be batched up in some way? Do you _really_ need to represent each individual update as a separate redraw of the UI?

Beyond that, I'd suggest that you look at these resources on Redux-related performance:

I'd also be happy to chat about this more over in the Reactiflux chat channels.

@markerikson Unfortunately yes, the concept of the game involves many fast updates to a relatively large data structure.
I realise I wasn't totally clear in my original comment, when I say 10-100 changes, these would not all trigger redraws. The 10-100 changes are the state changes which would be represented by a single redraw per 100ms.
So my question is really, if I were to batch up my updates into 10-100 state changes per 100ms, would the state management within redux be able to effectively process these in a way which would be sensibly completed within a 100ms'esque window to allow a redraw without things starting to lag behind.
The state updates would basically involve changing 3-4 properties in an array of size 8000, so we'd be talking about creating a new array 1-10 times per ms (assuming we want to keep things immutable) with some changes to one of the objects within the array index. The objects are quite small (3-4 properties, a couple of numbers and a couple of small strings)

This is why I wondered about the benefits of using immutable as Dan suggested as if we can re-use as much of the array as possible this will likely improve the performance.

@dougajmcdonald : well, "the state management in Redux" is really just the one root reducer function _you've_ provided :)

Immutable.js is not a magic perf-improving silver-bullet. It can make some things faster, and other things slower. I've got multiple articles on Immutable.js-related perf considerations you might want to look at.

To be honest, given your use case, you _could_ in theory get away with direct mutation more if you wanted to. Per my post Idiomatic Redux, Part 1 - Implementation and Intent, the Redux core itself doesn't actually care about mutation at all - it's primarily the React-Redux UI layer that does, as well as the DevTools. Now, mutation isn't how you're _intended_ to use Redux, but it's possible.

My personal advice would be to start simple. Just use plain JS objects and arrays. Update them immutably, either "by hand" or using one of the many immutable update utility libraries available. See how well that performs for you.

Then, from there, you can do additional optimization work in terms of batching, cutting down on dispatches, update logic, and so on.

@markerikson subtle jibe taken on the nose! :p Yeah you're right the reducer side of things at the moment is basically the creation and destruction of a fairly large array but I think the real issue is more likely the react component lifecycle bits and bobs when the end goal is going to be a canvas element ideally rendering at 60ish FPS.
Thanks for the point on immutable and the points on direct mutation I will read up a bit and consider these too.
At the moment my thought process is to disconnect the redux state from the canvas and just pass messages to it via pub/sub (which is how we have it hooked up already) just with more moving parts

I'm thinking of changing from:
pubsub > dispatch() > reducer > redux state > react component stuff > canvas render.

To:
pubsub > local component state > canvas render

I will still use redux for the rest of the UI, just perhaps not the canvas part of the app.

Your questions are totally valid. Actually the vuejs implementation of redux called vuex works under the concept of mutation. So the 'reducers' are in fact called mutations and as long as you modify the state in a centralized place everything should be fine. This happens because vue adds observers and other stuff to the state of the app so as you mentioned it makes more sense in the cae of vue to modify rather than replace the object. Redux is inmutable and at the end the performance of both approaches react VS vue virtual DOM updates is mostly the same at the point that is not possible to say that for all cases one is better than the other

Was this page helpful?
0 / 5 - 0 ratings

Related issues

parallelthought picture parallelthought  ·  3Comments

rui-ktei picture rui-ktei  ·  3Comments

CellOcean picture CellOcean  ·  3Comments

vraa picture vraa  ·  3Comments

benoneal picture benoneal  ·  3Comments