如何使用嵌套对象编写 state reducer
how to write state reducer with nested objects
我的 redux 状态树心智模型看起来像这样:
{
selectedDepartment: 'Chemistry',
purchasesByDepartment: {
Chemistry: {
purchaseOrders: {
...
},
invoices: {
...
}
},
Biology: {
purchaseOrders: {
...
},
invoices: {
...
}
},
...(another department, etc)
}
}
这是我的减速器
const purchasesByDepartment = (state = {}, action) => {
switch (action.type) {
case RECEIVE_POS:
return Object.assign({}, state, {
[action.department]: {
purchaseOrders: action.json
// but this wipes out my invoices
}
})
case RECEIVE_INVOICES:
return Object.assign({}, state, {
[action.department]: {
invoices: action.json
// but this wipes out my purchaseOrders
}
})
default:
return state
}
}
你可以看到在每个部门,我有一个 purchaseOrders 和一个 invoices 键。
我正在尽最大努力编写我的减速器,使其不会改变我的状态,但我并不走运,因为目前每个动作都会清除另一个键。
我启动了两个操作:RECEIEVE_POS、RECEIVE_INVOICES。
当我的操作 RECEIVE_POS 被调度时,我可以创建一个新的采购订单状态,但这会清除我的发票。
当我的操作 RECEIVE_INVOICES 被发送时,我可以创建一个新的发票状态,但这会清除我的采购订单。
如何编写我的 reducer 以便我可以保留我的采购订单和发票(如果它们已经存在于我的州)?
您可以先通过应用更改为部门创建新状态,然后将其合并到 reducer 状态。
const purchasesByDepartment = (state = {}, action) => {
switch (action.type) {
case RECEIVE_POS:
let nextPOS = Object.assign({}, state[action.department] || {}, {
purchaseOrders: action.json
});
return Object.assign({}, state, nextPOS);
default:
return state
}
}
就我个人而言,我喜欢通过使用 immutablejs
让我的整个商店不可变,这使得像这样的操作(以及其他一些事情)变得微不足道:
return state.setIn([action.department, 'purchaseOrders'], action.json]);
我使用扩展运算符(表示为...arrayName
)来实现这一点。是 es6,我用 babel 启用了功能。
Spread
会将数组展开到其元素中,因为它比 Object.assign
更具人类可读性。
使用你的上下文,对于我的减速器中的一个简单场景,我只是喜欢:
return {
...state,
selectedDepartment: 'Chemistry or whatever'
}
这将return一个由状态组成的数组,加上用给定值覆盖的selectedDepartment。
对于嵌入式对象你可以这样写:
return {
...state,
purchasesByDepartment: {
...state.purchasesByDepartment,
Chemistry: {
...state.purchasesByDepartment.Chemistry,
invoices: {
{your new invoices object comes here}
}
}
}
}
对象传播结合计算的属性名称帮助我解决了这个问题
case RECEIVE_POS:
return {
...state,
[action.department]: {
...state[action.department],
purchaseOrders: action.json
}
}
case RECEIVE_INVOICES:
return {
...state,
[action.department]: {
...state[action.department],
invoices: action.json
}
}
我的 redux 状态树心智模型看起来像这样:
{
selectedDepartment: 'Chemistry',
purchasesByDepartment: {
Chemistry: {
purchaseOrders: {
...
},
invoices: {
...
}
},
Biology: {
purchaseOrders: {
...
},
invoices: {
...
}
},
...(another department, etc)
}
}
这是我的减速器
const purchasesByDepartment = (state = {}, action) => {
switch (action.type) {
case RECEIVE_POS:
return Object.assign({}, state, {
[action.department]: {
purchaseOrders: action.json
// but this wipes out my invoices
}
})
case RECEIVE_INVOICES:
return Object.assign({}, state, {
[action.department]: {
invoices: action.json
// but this wipes out my purchaseOrders
}
})
default:
return state
}
}
你可以看到在每个部门,我有一个 purchaseOrders 和一个 invoices 键。 我正在尽最大努力编写我的减速器,使其不会改变我的状态,但我并不走运,因为目前每个动作都会清除另一个键。
我启动了两个操作:RECEIEVE_POS、RECEIVE_INVOICES。
当我的操作 RECEIVE_POS 被调度时,我可以创建一个新的采购订单状态,但这会清除我的发票。
当我的操作 RECEIVE_INVOICES 被发送时,我可以创建一个新的发票状态,但这会清除我的采购订单。
如何编写我的 reducer 以便我可以保留我的采购订单和发票(如果它们已经存在于我的州)?
您可以先通过应用更改为部门创建新状态,然后将其合并到 reducer 状态。
const purchasesByDepartment = (state = {}, action) => {
switch (action.type) {
case RECEIVE_POS:
let nextPOS = Object.assign({}, state[action.department] || {}, {
purchaseOrders: action.json
});
return Object.assign({}, state, nextPOS);
default:
return state
}
}
就我个人而言,我喜欢通过使用 immutablejs
让我的整个商店不可变,这使得像这样的操作(以及其他一些事情)变得微不足道:
return state.setIn([action.department, 'purchaseOrders'], action.json]);
我使用扩展运算符(表示为...arrayName
)来实现这一点。是 es6,我用 babel 启用了功能。
Spread
会将数组展开到其元素中,因为它比 Object.assign
更具人类可读性。
使用你的上下文,对于我的减速器中的一个简单场景,我只是喜欢:
return {
...state,
selectedDepartment: 'Chemistry or whatever'
}
这将return一个由状态组成的数组,加上用给定值覆盖的selectedDepartment。
对于嵌入式对象你可以这样写:
return {
...state,
purchasesByDepartment: {
...state.purchasesByDepartment,
Chemistry: {
...state.purchasesByDepartment.Chemistry,
invoices: {
{your new invoices object comes here}
}
}
}
}
对象传播结合计算的属性名称帮助我解决了这个问题
case RECEIVE_POS:
return {
...state,
[action.department]: {
...state[action.department],
purchaseOrders: action.json
}
}
case RECEIVE_INVOICES:
return {
...state,
[action.department]: {
...state[action.department],
invoices: action.json
}
}