具有不可变JS的Redux

时间:2016-04-12 11:01:52

标签: redux immutable.js

在Redux中使用immutablejs时,我们将从combineReducers中获取一个常规的javascript对象,这意味着即使其中包含所有内容,它也不会是一个不可变的数据结构。这是不是意味着使用immutablejs将是徒劳的,因为无论如何都会在每个动作上创建一个全新的状态对象?

示例:

code

2 个答案:

答案 0 :(得分:6)

  

将在每个动作上创建一个全新的状态对象

是,但不会重新创建由combineReducers分配的状态切片。这有点类似于这样做:

const person = { name: 'Rob' };
const prevState = { person };
const nextState = { person };

我创建了一个新的状态对象(nextState),但其person键仍设置为与prevState的{​​{1}}键完全相同的对象。内存中只有一个字符串person的实例。

问题是当我改变'Rob'对象时,我正在为多个状态改变它:

person

回到Redux,一旦第一次调用所有reducers,他们就会初始化应用程序状态的切片,而你的应用程序的整个状态基本上等于这个:

const person = { name: 'Rob' };
const prevState = { person };
person.name = 'Dan'; // mutation
const nextState = { person };

console.log(prevState.person.name); // 'Dan'

请注意,这是一个普通对象。它具有保存Immutable对象的属性。

当一个动作被调度并通过每个reducer时,你得到它的方式,reducer只是再次返回现有的Immutable对象,它不会创建一个新的。然后将新状态设置为具有属性{ firstReducer: Immutable.Map({greeting : 'Hey!'}), secondReducer: Immutable.Map({foo : 'bar'}), } 的对象,简单地指向前一个状态指向的同一个Immutable对象。

现在,如果我们没有对firstReducer使用Immutable,那该怎么办?

firstReducer

同样的想法,当首次调用reducer时,用作状态默认值的对象只是从前一个状态传递到下一个状态。内存中只有一个对象具有键const firstReducer = (state = {greeting : 'Hey!'}) => { return state } 和值greeting。有许多状态对象,但它们只有一个指向同一对象的键Hey!

这就是为什么我们需要确保我们不会意外地改变它,而是在我们改变它时更换它。你可以通过小心谨慎地完成这一点,但是使用Immutable使它更加万无一失。如果没有不可变,就可能搞砸了,并且这样做:

firstReducer

正确的方法是创建一个新的状态切片:

const firstReducer = (state = {greeting : 'Hey!'}, action) => {
  switch (action.type) {
    case 'CAPITALIZE_GREETING': {
      const capitalized = state.greeting.toUpperCase();
      state.greeting = capitalized; // BAD!!!
      return state;
    }
    default: {
      return state;
    }
  }
}

Immutable给我们的另一个好处是,如果我们的reducer的状态切片碰巧除了const firstReducer = (state = {greeting : 'Hey!'}, action) => { switch (action.type) { case 'CAPITALIZE_GREETING': { const capitalized = state.greeting.toUpperCase(); const nextState = Object.assign({}, state, { greeting: capitalized, }; return nextState; } default: { return state; } } } 之外还有很多其他数据,那么Immutable可能会在引擎盖下进行一些优化,以便它没有如果我们所做的只是更改单个值,并且仍然同时确保不变性,则重新创建每个数据。这很有用,因为它可以帮助减少每次调度操作时放入内存的内容量。

答案 1 :(得分:2)

Redux附带的

combineReducers()确实为您提供了一个普通的根对象。

这本身不是问题 - 只要不改变它们就可以将普通对象与Immutable对象混合。所以你可以忍受这个就好了。

您也可以使用返回不可变地图的alternative combineReducers()。这完全取决于你,并没有任何重大区别,只是它允许你在任何地方使用Immutable。

不要忘记combineReducers()实际上是easy to implement on your own

  

这是不是意味着使用immutablejs是徒劳的,因为无论如何都会在每个动作上创建一个全新的状态对象?

我不确定你的意思是“徒劳”。 Immutable不会神奇地阻止创建对象。当您在不可变set()Map时,您仍然会创建一个新对象,只是(在某些情况下)更有效。无论如何,当你有两把钥匙时,这并没有什么不同。

所以在这里使用Immutable并没有任何缺点,除了你的应用程序在选择数据结构时稍微不那么一致。