Javascript 在Redux reducer中何时以及哪些状态部分应该被深度克隆?

Javascript 在Redux reducer中何时以及哪些状态部分应该被深度克隆?,javascript,reactjs,redux,Javascript,Reactjs,Redux,很长一段时间以来,我一直在这样写我的简化: const init = { a: 'b' } const reducerName = function (state = init, action) { let newState = _.cloneDeep(state) // using lodash case 'ACTION_NAME': newState.a = 'c' return newState default: return state } 有

很长一段时间以来,我一直在这样写我的简化:

const init = {
  a: 'b'
}

const reducerName = function (state = init, action) {
  let newState = _.cloneDeep(state) // using lodash
  case 'ACTION_NAME':
    newState.a = 'c'
    return newState
  default:
    return state
}
有一天我意识到,这可能是一种非常愚蠢的方法,因为每次触发动作时,我都会在每个减速器中创建一个新对象,即使状态根本不会改变

所以,我的同事给我发了丹·阿布拉莫夫的推特,他说没有必要深度克隆这个州。这让我思考,何时以及如何进行深度克隆

假设我在减速器中有这种状态:

const init = {
  very: {
    deeply: {
      nested: 'string'
    }
  }
  notSoDeeplyNested: 'string'
}
那么,以下哪一项是管理状态的正确方法/最接近正确方法:

(一)

(二)

(三)

最后一个似乎对我来说都不合适,但我很难理解这一点


所以,据我所知,至少每次都要做一个浅拷贝,但是什么时候我应该深度克隆某个东西,它应该是对象的哪一部分?我指的是哪一部分是第一级对象还是仅仅是嵌套的部分?

回答你的问题:不,你没有深度克隆。相反,您可以选择性地浅复制需要更改的零件。在保留对未更改零件的现有引用的同时,可以使用“分解”仅以发生更改的路径为目标

在下面的示例中,它将
init
复制到一个对象,但将
very
替换为一个新对象。
非常
对象也是如此。它从
init.very
复制所有内容,但用新值替换
deep

case 'ACTION_NAME':
  return {
    ...init,
    very: {
      ...init.very,
      deeply: action.deeply
    }
  };
在ES5中可以这样翻译:

case 'ACTION_NAME':
  return Object.assign({}, init,
    {very: Object.assign({}, init.very,
      {deeply: action.deeply})});
最后,您有了一个新的状态对象,它部分由现有值和引用组成,部分由更改的值组成,类似于持久数据结构。我们更改了导致更改数据的对象,但并非所有对象都更改到可以称为深度克隆的程度

对于深层结构,它可能会变得冗长,但不是嵌套很深的结构。

你说的“正确的方式”是什么意思?您是否关心性能、代码可读性等。?
const reducerName = function (state = init, action) {
  case 'ACTION_NAME':
    let newDeeply = _.cloneDeep(state.very.deeply)
    let newState = Object.assign({}, state, { very: { deeply: newDeeply } ) // Cloning only the nested part, which actually changes?
    newState.very.deeply = action.deeply
    return newState
  default:
    return state
}
case 'ACTION_NAME':
  return {
    ...init,
    very: {
      ...init.very,
      deeply: action.deeply
    }
  };
case 'ACTION_NAME':
  return Object.assign({}, init,
    {very: Object.assign({}, init.very,
      {deeply: action.deeply})});