【问题标题】:Making an Reducer immutable in Redux在 Redux 中使 Reducer 不可变
【发布时间】:2016-06-25 14:03:36
【问题描述】:

我刚刚开始使用 ImmutableJS,我想制作一个不可变的 reducer。我已经读到我需要将减速器转换回来才能使其正常工作。谁能想出如何使这项工作的答案?本身我收到错误消息:“INCREASE_NUMBER.toObject() 不是函数”这是代码(如下):

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

【问题讨论】:

  • “使reducer 不可变”是什么意思?通常它是不可变的状态
  • 有点像你不知道什么是不变性
  • 这就是我问这个问题的原因,以了解 ImmutableJS ;)

标签: javascript reactjs redux immutable.js


【解决方案1】:

根据您的问题,您似乎不太了解不可变性,因此我首先质疑您需要使用 ImmutableJS 的合法性。您不必使用 ImmutableJS 来编写非变异状态转换。

我不需要插座盖来防止自己将金属物体插入电源插座。我宁愿不触电,所以我不会随便往插座里戳东西。没有比这更复杂的了。

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

你的 reducer 可以用严肃的 vanilla 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。了解您正在使用的工具。确定各种方法的优缺点。了解各种技术的“成本”。知道何时何地可以做出妥协。如果你最终还是决定使用 ImmutableJS,你就会知道为什么你选择了它,你也会知道它带来的收益/损失。

【讨论】:

  • 在这种情况下,我试图掌握 ImmutableJS 而不是常规 JS。原因很简单,要在更大范围内使用它。不过,感谢您的反馈!
  • 当我看到“all willy nilly”这句话时,我就知道我必须投票赞成。 (但你的其余答案也很有意义......)
  • 谢谢你...我同时跳入了 Redux 和 Immutable.js,并且对所有的 getter 和 setter 以及在运行时难以查看状态感到沮丧... .这就是我需要的——退后一步,考虑在没有库的情况下实现不变性(并在运行时拥有可查看的对象)
【解决方案2】:

reducer 是一个函数 - 一个不保持任何状态的纯函数。例如。就 reducer 而言,不变性没有意义。

不变性是指数据结构,而不是函数。在 React 和 Redux 中,让你的状态不可变是一个很好的做法,例如以始终返回不可变数据结构的方式设计您的 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() 返回一个新的不可变实例)。

这个 reducer 函数总是返回不可变的数据,这很棒。这就是在 reducer 中实现不可变性的方式:它们的返回值应该是不可变的。

【讨论】:

  • 感谢 iaretiga 的回答,不幸的是 state.update 没有更新号码:/有什么建议吗?
  • 甚至 André Stensberg:它不会更新原始状态的数字,因为它是一个不可变的 Map(例如,它不能更改或变异)。它返回一个新的不可变 Map,其中包含一个更新的数字。检查这个小提琴(ES5,但其他代码相同):Immutable reducer demo
  • 你能重构代码吗?对我来说,它看起来有点难以阅读。为什么要设置状态||地图?
  • 试图说 state.update('number', function(num) { return num + 1 });对我不起作用。有没有办法做到这一点,也许用 merge() ?
  • 现在可以使用 @iaretiga 了,我在 mapDispatchToProps 中使用了 value: state.number。感谢您的帮助!
【解决方案3】:

在你这里,INCREASE_NUMBER 只是一个函数,所以在它上面调用toObject 是没有意义的。

据我了解,toObject 是一个可以在不可变对象上调用以将其转换为 JS 对象的函数。但是,还有一个merge 函数用于扩展Map,状态值似乎是Map,而不是JS 对象,那么为什么要在这里的任何地方使用toObject

只需删除 toObject 行。

如果意图是让reducer 返回一个JS 对象而不是Map,您必须将它放在return 语句中的函数内。但是,使用 Map 并返回一个对象似乎是 reducer 的错误方法。进出的状态应该是一样的形状。

【讨论】:

  • 你能用一个代码示例编辑你的评论让我完全理解吗?也尝试了 .toJS() 但得到了同样的错误。
  • 不要在函数上调用任何东西。调用返回值。例如。 return state.toObject()return state.merge(...).toObject()
  • 做到了,现在 state.merge 不是一个函数。有什么建议吗?
  • 去掉了merge,换成了Map,现在好像可以了,但是为什么merge无效呢?
  • 我强烈建议在你的 reducer 代码中避免使用 toJS 和 toObject。它们是性能问题的第一来源。如果您想始终对普通对象进行操作,您当然不必使用 Immutable.js 将您的数据视为不可变的。当您拥有像 Map 和 List 这样的集合并且您希望对其进行不可变操作时,Immutable.js 非常有用。那么你应该始终将它们视为 Immutable.js 集合,并避免转换为普通的 JS 对象。
猜你喜欢
  • 2019-08-25
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 2016-06-21
  • 1970-01-01
  • 2020-08-25
  • 1970-01-01
相关资源
最近更新 更多