89

So I started learning React a week ago and I inevitably got to the problem of state and how components are supposed to communicate with the rest of the app. I searched around and Redux seems to be the flavor of the month. I read through all the documentation and I think it's actually a pretty revolutionary idea. Here are my thoughts on it:

State is generally agreed to be pretty evil and a large source of bugs in programming. Instead of scattering it all throughout your app Redux says why not just have it all concentrated in a global state tree that you have to emit actions to change? Sounds interesting. All programs need state so let's stick it in one impure space and only modify it from within there so bugs are easy to track down. Then we can also declaratively bind individual state pieces to React components and have them auto-redraw and everything is beautiful.

However, I have two questions about this whole design. For one, why does the state tree need to be immutable? Say I don't care about time travel debugging, hot reload, and have already implemented undo/redo in my app. It just seems so cumbersome to have to do this:

case COMPLETE_TODO:
  return [
    ...state.slice(0, action.index),
    Object.assign({}, state[action.index], {
      completed: true
    }),
    ...state.slice(action.index + 1)
  ];

Instead of this:

case COMPLETE_TODO:
  state[action.index].completed = true;

Not to mention I am making an online whiteboard just to learn and every state change might be as simple as adding a brush stroke to the command list. After a while (hundreds of brush strokes) duplicating this entire array might start becoming extremely expensive and time-consuming.

I'm ok with a global state tree that is independent from the UI that is mutated via actions, but does it really need to be immutable? What's wrong with a simple implementation like this (very rough draft. wrote in 1 minute)?

var store = { items: [] };

export function getState() {
  return store;
}

export function addTodo(text) {
  store.items.push({ "text": text, "completed", false});
}

export function completeTodo(index) {
  store.items[index].completed = true;
}

It's still a global state tree mutated via actions emitted but extremely simple and efficient.

MarredCheese
  • 9,495
  • 5
  • 59
  • 63
Ryan Peschel
  • 9,095
  • 18
  • 57
  • 101
  • 2
    "For one, why does the state tree need to be immutable?" --- then you must provide an algorithm to determine if data has changed. It's not possible to implement it for an arbitrary data structure (if it's mutable). Take `immutablejs` and use `return state.setIn([action.index, 'completed'], true);` to reduce boilerplate. – zerkms Oct 29 '15 at 20:46
  • 1
    PS: `return state.map(i => i.index == action.index ? {...i, completed: true} : i);` – zerkms Oct 29 '15 at 20:52

1 Answers1

56

Isn't Redux just glorified global state?

Of course it is. But the same holds for every database you have ever used. It is better to treat Redux as an in-memory database - which your components can reactively depend upon.

Immutability enables checking if any sub-tree has been altered very efficient because it simplifies down to an identity check.

Yes, your implementation is efficient, but the entire virtual dom will have to be re-rendered each time the tree is manipulated somehow.

If you are using React, it will eventually do a diff against the actual dom and perform minimal batch-optimized manipulations, but the full top-down re-rendering is still inefficient.

For an immutable tree, stateless components just have to check if the subtree(s) it depends on, differ in identities compared to previous value(s), and if so - the rendering can be avoided entirely.

MarredCheese
  • 9,495
  • 5
  • 59
  • 63
lorefnon
  • 12,112
  • 4
  • 54
  • 87
  • 3
    Isn't this a bit of premature optimization though? Also, how do we know that the cost of constantly duplicating immutable objects is less than re-rendering the DOM (also wouldn't React's Virtual DOM heavily mitigate this cost?) – Ryan Peschel Oct 29 '15 at 21:16
  • 3
    Well, GUI libraries this kind of optimization for a long time (Refer: http://bitquabit.com/post/the-more-things-change/) Plus management of an immutable data structure is not as costly as you might think - for example if a node gets changed, only a single chain of parents needs to chain - rest of the nodes remain unaffected. So we are not duplicate the *entire* data structure for every action - we reuse the sub-components that haven't changed to build a new data structure. – lorefnon Oct 29 '15 at 21:27
  • What about my array of brush strokes? Is there an immutable and efficient way to duplicate this array of hundreds of items when just adding one item to the end of the array? – Ryan Peschel Oct 29 '15 at 21:29
  • 4
    Also Reacts Virtual DOM thing is not exactly dark magic - Quoting from React docs: "Generating the minimum number of operations to transform one tree into another is a complex and well-studied problem - The state of the art algorithms have a complexity in the order of O(n3) where n is the number of nodes in the tree. " – lorefnon Oct 29 '15 at 21:30
  • 2
    The reason React is able to perform much better in practice is because : React relies on heuristics - so: "If you don't provide stable keys (by using Math.random() for example), all the sub-trees are going to be re-rendered every single time. By giving the users the choice to choose the key, they have the ability to shoot themselves in the foot." So just like you can help React by providing stable keys, in the same way you can help React by providing immutable data props. – lorefnon Oct 29 '15 at 21:30
  • 1
    Regarding your array of brush strokes -- please refer : https://facebook.github.io/immutable-js/docs/#/List Quoting from docs: Lists are ordered indexed dense collections, much like a JavaScript Array. Lists implement Deque, with efficient addition and removal from both the end (push, pop) and beginning (unshift, shift). – lorefnon Oct 29 '15 at 21:32
  • Ok then. I'm not quite sure I understand all the reasons just yet but if immutability is seen by all to be the better solution then I'll go with it and hopefully have an epiphany later. Thanks all. Also O(1) push on an immutable list is nice! – Ryan Peschel Oct 29 '15 at 21:45
  • 1
    it is a 'glorified global state' but with the addition of reducers its effectively a deterministic state machine for which your UI is a visualisation of. – James Cat Aug 30 '17 at 14:44