在Redux中更新嵌套状态的更干净/更短的方法
Cleaner/shorter way to update nested state in Redux?
有时减速器会有点乱:
const initialState = {
notificationBar: {
open: false,
},
};
export default function (state = initialState, action) {
switch (action.type) {
case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
return Object.assign({}, state, {
// TODO: Find a cleaner way to do this!
notificationBar: Object.assign({}, state.notificationBar, {
open: true,
}),
});
default:
return state;
}
}
有更简洁的方法吗?
UPD:它现在是ES2018 的一部分
它可能会通过非标准化而属性扩展语法略有改进:
return {
...state,
notificationBar: {
...state.notificationBar,
open: true,
},
};
尽管可以使用扩散运算符,但有很多其他方法可以实现相同的结果,甚至不需要未来的JS编译器来实现非标准化功能。以下是其他一些没有特殊顺序的选项。
返回文字
如果您确信您的状态不会增长,那么您可以简单地将整个新状态作为文字返回。
return {
notificationBar: {
open: true
}
}
然而,这通常并不合适,因为你的状态不太可能如此简单。
联合收割机减速器
Redux为您提供了一种实用方法,用于组合在状态对象的不同部分上工作的几个减速器。在这种情况下,您将创建一个单独处理该对象的notificationBar
reducer。
createStore(combineReducers({
notificationBar: function(state=initialNBarState, action) {
switch (action.type) {
case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
return Object.assign({}, state, { open: true });
}
});
这样就不用担心顶级属性,这样就可以避免嵌套对Object.assign
的调用。
如果您的状态可以从逻辑上分解为明确定义的部分,那么这可能是解决此问题的最常用方法。
使用不可变数据
您可以使用持久数据结构库来创建可以修改以返回副本的数据结构。
森喜朗
Mori是将Clojure的数据结构和功能API编译成JS的结果。
import { hashMap, updateIn } from 'mori';
const initialState = hashMap(
"notificationBar", hashMap(
"open", false
)
);
// ...
return updateIn(state, ['notificationBar', 'open'], true);
不可变JS
ImmutableJS是将Clojure的持久数据结构中的Hash Array Mapped Tries的语义引入Javascript的一种更为必要的方法。
import { Map } from 'immutable';
const initialState = Map({
notificationBar: Map({
open: true
});
});
// ...
return state.setIn(['notificationBar', 'open'], true);
别名Object.assign
您可以创建一个更友好的Object.assign
版本来编写上面代码的简洁版本。事实上,它可以和...
运算符一样简洁。
function $set(...objects) {
return Object.assign({}, ...objects);
}
return $set(state, {
notificationBar: $set(state.notificationBar, {
open: true,
})
});
使用不可变帮助程序
有许多库还提供了不变性助手,用于修改常规可变对象。
react插件更新
长期以来,React一直有一套内置的不变性助手。它们使用与MongoDB查询类似的语法。
import update from 'react-addons-update';
return update(state, {
notificationBar: {
open: { $set: true }
}
});
点道具不可变
此库允许您使用熟悉的点路径来指定对(嵌套(特性的更新。
import dotProp from 'dot-prop-immutable';
return dotProp.set(state, 'notificationBar.open', true);
在中更新
该库是react-addons-update
的包装器,为更新(嵌套(属性提供了更实用的语法。
您传递的不是一个新值,而是一个接受旧值并返回新值的函数。
import updateIn from 'update-in';
return updateIn(state, ['notificationBar', 'open'], () => true);
不可变路径
对于更新属性,这个库就像是dot-prop-immutable
和update-in
的交叉。
import path from 'immutable-path';
return path.map(state, 'notificationBar.open', () => true);
您可以使用Lenses。
import { set, makeLenses } from '@DrBoolean/lenses'
const L = makeLenses(['notificationBar', 'open']);
const notificationBarOpen = compose(L.notificationBar, L.open)
const setNotificationBarOpenTrue = set(notificationBarOpen, true)
const a = { notificationBar: { open: false } }
const b = setNotificationBarOpenTrue(a)
// `a` is left unchanged and `b` is `{ notificationBar: { open: true } }`
您可以将Lenses视为合成属性访问/更新。
关于镜片的一些好资源:
- 视频镜头快速变脏
- 实用文章镜头和虚拟DOM支持Open Closed
- 视频非数学家的函数编程模式
- 关于Lenses与imutable.js结合的文章
如果你能阅读lisps,我还建议你看看球拍文档中关于镜片的精彩介绍。最后,如果你想更深入地阅读haskell,你可以观看:Lenses-合成数据访问和操作。
如果您正在使用Immutable.js,您可以在嵌套结构主题下查看一些可能对您有所帮助的函数,我个人使用mergeDeep
:
prevState.mergeDeep({ userInfo: {
username: action.payload.username,
} }),
除了前面所说的,以下是Ramda的一种功能方式:
import { assocPath } from 'ramda';
const o1 = { a: { b: { c: 1 }, bb: { cc: 22 } } };
const o2 = assocPath(['a', 'b', 'c'])(42)(o1);
console.log(o1 !== o2, o1.a !== o2.a); // new copies of "changed" objects
console.log(o1.a.bb === o2.a.bb); // deep unchanged properties are copied by reference
这里的所有建议都很好且有效,但我想提供另一个解决方案。这里出现的问题肯定是一种常见的模式,所以我认为最好只为这种更新编写自己的界面,并在减速器中使用它,并使用一个函数在所有减速器中进行深度更新。
例如,我创建了一个库,在那里我试图以下一种方式解决这个问题:我获得模块的类型(所谓的"tile"(、执行操作的函数(异步和同步(以及基于传递的参数的所需嵌套。因此,对于您的情况,它将类似于:
import { createSyncTile } from 'redux-tiles';
const uiTile = createSyncTile({
type: ['ui', 'elements'],
fn: ({ params }) => params,
// type will be `notificationBar`
nesting: ({ type }) => [type],
});
就是这样——它将在任意嵌套中正确更新。此外,tile提供了选择器,所以您个人不必担心数据的确切位置,只需使用它们即可。所以,我不想说这是最好的解决方案,但想法很简单——不要害怕编写自己的实现,然后使用工厂来解决这个问题。
- Redux-组件不同的方法(智能/哑点/容器/演示)
- 检查 redux 状态是否更改的简单方法
- 将 react-redux 与基于事件的第三方库一起使用的最佳方法是什么?
- 有没有更好的方法来处理窗口属性&React/Redux中的子组件
- 为什么在Redux存储中使用字符串而不是直接方法调用来触发操作
- 在 redux 化简器中更新状态的正确方法
- 在Redux中更新嵌套状态的更干净/更短的方法
- React+Redux.执行容器和转储组件中定义的所有方法/操作
- 使用redux实现事务的正确方法是什么
- 在尝试管理和访问不同组件和处理程序方法中的状态时,我可以使用RxJS与Redux/context吗
- 处理react redux中的获取错误的最佳方法是什么
- Redux:在reducer中过滤数据数组的正确方法是什么
- 在React+Redux.js应用程序中,在Provider组件之外操作DOM元素的正确方法是什么
- React-Redux's connect()方法抛出TypeError
- 写数据到React-redux中存储的最简单的方法是什么?
- 是否有任何方法从存储读取而不是从组件在React-redux
- 如何将方法放在Redux状态的对象上
- 在Redux/Flux中,用于乐观更新的操作存储是一种很好的方法
- 为什么上面写着'next'在redux中间件代码中没有定义.中间件的next方法是否已弃用
- redux-react在react子组件中操作props的方法是什么?