在 Redux 中使 Reducer 不可变

Making an Reducer immutable in Redux

我刚开始使用 ImmutableJS,我想制作一个不可变的 reducer。我读过我需要将我的减速器转换回来才能使其工作。任何人都可以想出如何使这项工作的答案吗?本身我收到错误消息:"INCREASE_NUMBER.toObject() is not a function" 这是代码(如下):

import { INCREMENT } from '../actionTypes/default'
import { Map, toObject } from 'immutable'

const INCREASE_NUMBER = (state = Map({number: 0}), action) => {
  switch(action.type) {
    case INCREMENT:
    return state.merge({number: state.number + 1})
    default:
    return state
  }
};
INCREASE_NUMBER.toObject()

module.exports = INCREASE_NUMBER

在你这里的例子中,INCREASE_NUMBER 只是一个函数,所以调用 toObject 是没有意义的。

据我了解,toObject是一个函数,可以在不可变对象上调用,将它们转换为JS对象。但是,还有一个merge函数,用来扩展一个Map,状态值好像是一个Map,不是JS对象,为什么要用toObject 这里的任何地方?

只需删除 toObject 行。

如果目的是使 reducer return 成为 JS 对象而不是 Map,则必须将其放在 return 语句中的函数内。但是,使用 Map 和 returning 一个对象似乎是对 reducers 的错误方法。进出的状态应该是一样的形状。

reducer 是一个函数——一个不保持任何状态的纯函数。例如。不变性对于减速器而言没有意义。

不变性是指数据结构,而不是函数。在 React 和 Redux 中,让你的状态不可变是一个很好的做法,例如以始终 return 不可变数据结构的方式设计你的 reducer 函数。但这不适用于 reducer 本身——那只是一个函数。

import { INCREMENT } from '../actionTypes/default'
import { Map } from 'immutable'

export default (state = Map({number: 0}), action = {}) => {
    switch (action.type) {
        case INCREMENT:
            // return new Immutable data structure here
            return state.update('number', num => num + 1);
        default:
             return state;
    }
};

上面的示例与您的原始代码非常相似,只是我们不需要来自 immutableJS 的 toObject,并且我已经优化了状态更新。

这里什么是不可变的?全州:

  • 默认状态,作为 state 参数的默认值,是一个新的不可变 Map;
  • INCREMENT 操作后的修改状态(Map.prototype.update() return 是一个新的不可变实例)。

这个 reducer 函数总是 returns 不可变数据,这很棒。这就是在 reducer 的情况下实现不可变性的方式:它们的 return 值应该是不可变的。

根据你的问题,你似乎并没有真正理解不变性,因此我首先质疑你需要使用 ImmutableJS 的合法性。您不必使用 ImmutableJS 来编写非可变状态转换。

我不需要插座盖来防止自己将金属物体插入电源插座。我宁愿不被电死,所以我不会随便往插座里塞东西。没那么复杂了。

您最好深入了解您正在使用的工具。您应该知道 JavaScript operators/functions/methods 何时会改变数据,何时不会。如果你认为 ImmutableJS 可以保护你免受你自己的无知,那你就犯了一个大错误。您有时会 运行 遇到问题并且不知道如何诊断它们,因为您没有花时间了解自己在做什么。

你的减速器可以用严肃的香草来写JavaScript

import { INCREMENT } from '../actionTypes/default'

const initialState = {number: 0}

const increaseNumberReducer = (state, action) => {
  switch (action.type) {
    case INCREMENT:
      return {number: state.number + 1}
    default:
      return state
  }
}

export default increaseNumberReducer

这里没有突变。按照这段代码的编写方式,突变基本上是不可能的。我们正在 读取 来自 state 的值,但这里没有代码可以 写入 到现有的 state 对象。 state 不是 不可变的 对象,但您也不是愚蠢的开发人员。如果您编写了一些代码来改变 state 对象,那是您自己的错。如果你在电源插座上到处乱摸,只能怪你自己。

我本可以轻松写出

// non-mutating state transformation
case INCREMENT:
  return Object.assign({}, state, {number: state.number + 1})

...你应该知道这与

有何不同
// mutating state transformation
case INCREMENT:
  return Object.assign(state, {number: state.number + 1})

同样,你应该知道

// non-mutating array transformation
[1,2,3].concat([4])

...还有这个

// mutating array transformation
[1,2,3].push(4)

这个怎么样

// mutating sort
[4,2,1,3].sort()

...还有这个

// non-mutating sort
[4,2,1,3].slice(0).sort()

当然,如果您有一个大型应用程序,有人可能会争辩说在您的数据周围添加一个小的不变性安全预防措施是有意义的。这可能会阻止无知的开发人员 意外地 改变 state 他们不应该的。对我来说,这不是一个有效的解决方案。如果这是一个 redux 应用程序,你的开发人员应该对 redux 哲学有深入的了解,这意味着 reducer 是纯函数并且 state 永远不会发生变化......知识和智慧是无可替代的。

深入了解JavaScript。了解您正在使用的工具。确定各种方法的优缺点。了解各种技术的"costs"。知道何时何地可以做出妥协。如果你最终还是决定使用 ImmutableJS,你会知道 为什么 你选择它,你会知道它附带的 gains/losses。