98

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.

2
  • 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
    Commented Oct 29, 2015 at 20:46
  • 1
    PS: return state.map(i => i.index == action.index ? {...i, completed: true} : i);
    – zerkms
    Commented Oct 29, 2015 at 20:52

2 Answers 2

59

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.

8
  • 4
    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?) Commented Oct 29, 2015 at 21:16
  • 3
    Well, GUI libraries this kind of optimization for a long time (Refer: 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
    Commented Oct 29, 2015 at 21:27
  • 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
    Commented Oct 29, 2015 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
    Commented Oct 29, 2015 at 21:30
  • 1
    Regarding your array of brush strokes -- please refer : 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
    Commented Oct 29, 2015 at 21:32
0

Yes it is!!! Since there is no governance of who is allowed to write a specific property/variable/entry to the store and practically you can dispatch any action from anywhere, the code tends to be harder to maintain and even spaghetti when your code base grows and/or managed by more than one person.

I had the same questions and issues with Redux when I started use it so I have created a library that fix these issue:
It is called Yassi:
Yassi solves the problems you mentioned by define a globally readable and privately writable store. It means that anyone can read a property from the store (such as in Redux but simpler).
However only the owner of the property, meaning the object that declare the property can write/update that property in the store

In addition, Yassi has other perks in it such as zero boilerplate to declare entry in the store by using annotations (use @yassit('someName'))
Update the value of that entry does not require actions/reducers or other such cumbersome code snippets, instead just update the variable like in regular object.

Not the answer you're looking for? Browse other questions tagged or ask your own question.