无法将深克隆转换为沉浸式

时间:2018-09-19 20:31:32

标签: reactjs redux

我正在处理一些旧代码,这些代码用于对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
  },

2 个答案:

答案 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;
    });
  },
}