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

例子:

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

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

const rootReducer = combineReducers({
  firstReducer, secondReducer
})

最佳答案



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

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

我创建了一个新的状态对象 ( 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'

回到 Redux,一旦所有的 reducer 都被第一次调用,它们就会初始化它们的应用程序状态的切片,你的应用程序的整个状态基本上等于这个:
{
  firstReducer: Immutable.Map({greeting : 'Hey!'}),
  secondReducer: Immutable.Map({foo : 'bar'}),
}

请注意,这是一个普通对象。它具有保存不可变对象(immutable对象)的属性。

当一个 Action 被分派(dispatch)并通过每个 reducer 时,你得到它的方式, reducer 只是再次返回现有的不可变对象(immutable对象),它不会创建一个新的对象。然后将新状态设置为具有属性 firstReducer 的对象,该对象简单地指向前一个状态所指向的同一个不可变对象(immutable对象)。

现在,如果我们不为 firstReducer 使用 Immutable 会怎样:
const firstReducer = (state = {greeting : 'Hey!'}) => {
  return state
}

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

这就是为什么我们需要确保我们不会意外地改变它,而是在我们改变它时替换它。当然,只要小心一点,您可以在没有 Immutable 的情况下完成此操作,但使用 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;
    }
  }
}

正确的方法是创建一个新的状态切片:
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 给我们的另一个好处是,如果我们的 reducer 的状态切片碰巧除了 greeting 之外还有很多其他数据,Immutable 可能会在幕后做一些优化,这样它就不必重新创建每条数据,如果我们所做的只是改变一个值,同时仍然确保不变性。这很有用,因为它可以帮助减少每次分派(dispatch)操作时放入内存的内容量。

关于带有不可变 JS 的 Redux,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36571348/

10-11 09:24