我正在处理一些旧代码,这些代码用于对React应用程序中的redux状态进行完整的深度克隆。这是原始的样板,用作应用程序中使用的减速器的基础:
export default (initialState, handlers = {}, promiseActionTypes = []) =>
(state = initialState, action) => {
let nextState = _.cloneDeep(state)
promiseActionTypes.forEach((actionType) => {
if (action.type === `@@REMOTE/${actionType}_REQUEST`) {
nextState.isFetching = true
}
if (action.type === `@@REMOTE/${actionType}_SUCCESS`) {
nextState = {
...nextState,
data: _.get(action, 'data.storeData', action.data),
isFetching: false,
isInited: true,
}
}
if (action.type === `@@REMOTE/${actionType}_FAILURE`) {
nextState.isFetching = false
}
})
if (handlers.hasOwnProperty(action.type)) {
nextState = handlers[action.type](nextState, action, _.cloneDeep(state))
}
return nextState
}
这些克隆深处都是大禁忌,所以我试图利用immer的Produce函数在返回新状态之前对状态的草稿副本进行变异。
问题是,我无法使所有内容保持同步。一些状态无法在此处或此处正确更新。到目前为止,这是我重构的尝试:
import produce from 'immer'
export default (initialState, handlers = {}, promiseActionTypes = []) =>
(state = initialState, action) => {
return produce(state, (draft) => {
promiseActionTypes.forEach((actionType) => {
if (action.type === `@@REMOTE/${actionType}_REQUEST`) {
draft.isFetching = true
}
if (action.type === `@@REMOTE/${actionType}_SUCCESS`) {
draft.data = _.get(action, 'data.storeData', action.data)
draft.isFetching = false
draft.isInited = true
}
if (action.type === `@@REMOTE/${actionType}_FAILURE`) {
draft.isFetching = false
}
return draft
})
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](draft, action, state)
}
return draft
})
}
我试图解冻被触摸的物体,但仍然没有骰子。我的实现刚刚结束吗?还是我误解了农产品的生产方式?
地狱,如果我只是想让那两个lodash cloneDeep
通话离开那里,我是否需要像浸入式设备?
编辑:这是一个调用最后一行的自定义处理程序的示例:
LOCATION_CHANGE: (state, action) => {
// bootstrap
if (_.isUndefined(state.location)) {
state.location = action.location
}
state.next = {
location: action.location,
changed: action.changed,
}
state.isNavigating = true
return state
},
VIEW_ROUTE_MATCH: (state, action) => {
state.next = {
...state.next,
match: action.match,
view: action.view,
}
return state
},
答案 0 :(得分:0)
第一个问题是,您的原始代码实际上根本不应该进行“深度克隆”,尤其是不要在reducer的顶部。这意味着它为每个调度的动作都做了额外的工作,即使它不相关,也可能导致UI重新呈现,即使数据的值没有真正改变也是如此。因此,正如您所说,“大禁忌”。基于此,是的,我想说Immer在这里很有用。
第二,从概念上讲,您不应该使用promiseActionTypes.map()
,因为您尝试进行一些更新而不是返回新数组。与原始代码一样使用.forEach()
。
第三,您还没有描述哪些字段未正确更新,所以有点困难
除此之外,draft
代码看起来还不错。但是,在这种情况下,handlers
行看起来可疑。我的猜测是,这些其他处理程序可能正在做自己的不可变工作,而不是尝试“修改” draft
状态值。哦,您甚至都没有将draft
传递给他们。因此,如果您希望这些更改将引起其余更改,则需要A)将draft
传递给处理程序,并B)更改它们以“修改”草稿。
答案 1 :(得分:0)
在这种情况下,我想说您不需要不需要,因为您并没有真正进行深层或复杂的更改,但是它仍然可以帮助您整理零件。 Immer会强制执行三行代码,因此我会坚持使用散布运算符,除非它将其保持为一行。
export default (initialState, handlers = {}, promiseActionTypes = []) =>
(state = initialState, action) => {
let nextState = state;
promiseActionTypes.forEach((actionType) => {
if (action.type === `@@REMOTE/${actionType}_REQUEST`) {
nextState = { ...nextState, isFetching: true };
}
if (action.type === `@@REMOTE/${actionType}_SUCCESS`) {
nextState = produce(state, draft => {
draft.data = _.get(action, 'data.storeData', action.data);
draft.isFetching = false;
draft.isInited = true;
});
}
if (action.type === `@@REMOTE/${actionType}_FAILURE`) {
nextState = { ...nextState, isFetching: false };
}
})
if (handlers.hasOwnProperty(action.type)) {
nextState = handlers[action.type](nextState, action);
}
return nextState
}
const handlers = {
LOCATION_CHANGE: (state, action) => {
return produce(state, draft => {
if (_.isUndefined(state.location)) {
draft.location = action.location;
}
draft.next = {
location: action.location,
changed: action.changed,
}
draft.isNavigating = true
});
},
VIEW_ROUTE_MATCH: (state, action) => {
return produce(state, draft => {
draft.match = action.match;
draft.view = action.view;
});
},
}