具有不可变JS的Redux

swe*_*let 3 immutable.js redux

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

例:

const firstReducer = (state = Immutable.Map({greeting : 'Hey!'})) => {
  return state
}

const secondReducer = (state = Immutable.Map({foo : 'bar'})) => {
  return state
}

const rootReducer = combineReducers({
  firstReducer, secondReducer
})
Run Code Online (Sandbox Code Playgroud)

Rob*_*ise 7

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

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

const person = { name: 'Rob' };
const prevState = { person };
const nextState = { person };
Run Code Online (Sandbox Code Playgroud)

我创建了一个新的状态对象(nextState),但它的person重点仍然设置为完全相同的对象是prevStateperson关键.'Rob'内存中只有一个字符串实例.

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

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

console.log(prevState.person.name); // 'Dan'
Run Code Online (Sandbox Code Playgroud)

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

{
  firstReducer: Immutable.Map({greeting : 'Hey!'}),
  secondReducer: Immutable.Map({foo : 'bar'}),
}
Run Code Online (Sandbox Code Playgroud)

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

当一个动作被调度并通过每个reducer时,你得到它的方式,reducer只是再次返回现有的Immutable对象,它不会创建一个新的.然后将新状态设置为一个对象,该对象的属性firstReducer只是指向前一个状态所指向的同一个Immutable对象.

现在,如果我们不使用Immutable firstReducer:

const firstReducer = (state = {greeting : 'Hey!'}) => {
  return state
}
Run Code Online (Sandbox Code Playgroud)

同样的想法,首次调用reducer时用作状态默认值的对象只是从前一个状态传递到下一个状态.内存中只有一个对象具有键greeting和值Hey!.有许多状态对象,但它们只有一个firstReducer指向同一对象的键.

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

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Immutable给我们的另一个好处是,如果我们的reducer的状态切片碰巧还有很多其他数据greeting,那么Immutable可能会在引擎盖下进行一些优化,这样它就不必重新创建每一块数据.我们做的是更改单个值,但仍然同时确保不变性.这很有用,因为它可以帮助减少每次调度操作时放入内存的内容量.