也许这只是我的Redux知识中缺少的一条信息,但是即使经过数小时的搜索,我仍然不知道如何创建可重用的reducer来更新许多状态属性。
比方说,我创建了一个简单的组件MySwitch
,提供了用于编辑布尔值的UI。有一个相关的动作和一个reducer来更新状态。我了解如何使其适用于该州的特定物业。但是,如何创建该组件(和相关的reducer)以与商店中的任何布尔值一起使用,而又不为它们中的每一个创建一个特殊的reducer?
比方说,状态看起来像这样:
{
items: {
house: { isBig:true, location: ... },
car: { isMine:true, isBroken:false, ... }
},
books: [
{ id:1, available:true, title:... },
{ id:2, available:false, title:... }, ...
]
}
创建MySwitch
的各种实例以查看和编辑任何给定值(例如items.house.isBig
,items.car.isMine
或books[1].available
)并使用相同的减速器?
我可以使用connect将属性注入到组件中,例如:
ASwitch = connect(state => ({
valueToEdit: state.items.car.isMine
})(MySwitch)
但是我不知道如何将其传递给reducer以及如何让它更新状态的相应部分。我以为可以提供操作的路径(即"items.car.isMine"
)
export const editBoolean = ( path, value ) => {
return {
type: 'BOOL_EDIT',
payload: { path, value }
}
}
然后在reducer中使用类似的东西:
case 'BOOL_EDIT': {
const { path, value } = action.payload;
return {
...state,
[path]: value
}
}
但是它不起作用,似乎ES6不支持变量计算属性,而是创建了state["items.car.isMine"]
之类的属性。
我不知道该如何继续。感谢您的帮助。
答案 0 :(得分:0)
我认为您的想法正确,但是您的问题并不完全是针对redux的,您只需使用希望通过路径定位的更新节点克隆状态即可。
您可以使用类似的东西
const singlePathReducer = (state, { payload: { path, value } }) => path.reduce(({ nextState, branch = nextState }), node) => {
if (node !== path[path.length - 1]) {
// this was the part you were missing; you need to walk down your state tree
// by passing a reference of the current branch to the next reduce callback
return { nextState, branch: nextState[node] };
}
// mutate your nextState, this is ok
branch[node] = value;
return nextState;
// don't mutate state, create a copy
}, { nextState: { ...state } });
如果我没记错js中的按引用传递是如何工作的,这应该可以工作。完整的实现将是这样的(将singlePathReducer
fn分开保存可以使单元测试更加容易):
const rootReducer = (state, action) => {
switch action.type {
case 'BOOL_EDIT':
return singlePathReducer(state, action);
default:
return state;
}
};
答案 1 :(得分:0)
*免责声明:对我来说,这有点像是Redux的反模式,因为它允许通过一次操作将存储中的任何值设置为任何值。明智地使用它。
````
const set = require("lodash/set");
const { produce } = require("immer");
case 'BOOL_EDIT': {
const { path, value } = action.payload;
return produce(state, draft => {
set(draft, path, value);
});
}
```
editBoolean("items.car.isMine", false)