lodash的mergeWith

时间:2018-12-04 00:15:27

标签: javascript redux lodash

我正在使用lodash的mergeWith将一些有效负载数据合并到某些redux状态中。但是,这样做时,我最终会直接改变状态。我不知道这是怎么发生的,因为我正在使用{...state}进行合并。为什么会发生这种情况,我该怎么做才能不直接改变我的状态?您可以在下面的代码段中查看所发生情况的示例。谢谢!

const merger = (objectOne, objectTwo) => {
  const customizer = (firstValue, secondValue) => {
    return _.isArray(firstValue) ? secondValue : undefined;
  };

  return _.mergeWith(objectOne, objectTwo, customizer);
};

const state = {
  1: {a: true, b: true, c: true},
  2: {a: true, b: true, c: true},
  3: {a: true, b: true, c: true},
}

const payload = {
   2: {a: true, b: false, c: true},
}

console.log("Merged data:");
console.log(merger({...state}, payload));
console.log("Manipulated state:");
console.log(state);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>

2 个答案:

答案 0 :(得分:2)

您必须注意,传播语法{ ...state }仅对对象进行浅表复制。因此,实际上-深度嵌套的属性-在您的特定情况下{ a: true, b: true, c: true }仍然是通过引用保留的相同对象。如果您希望避免状态突变错误,则应使用例如cloneDeep来自lodash。

merger(_.cloneDeep(state), payload);

答案 1 :(得分:1)

这里是个问题:

let a = { foo: 'A'}
let c = { ... a }  // shallow copy of a

a.foo = 'boo'

console.log(a)
console.log(c) // works as expected c.foo is NOT changed and still is 'A'

从上面的示例中可以看到,具有传播和基于值的属性的浅表复制按预期方式工作。但是,当您这样做时:

let x = { foo: { boo: 'A' }}  // object as value this time
let y = { ... x }  // shallow copy of x

x.foo.boo = 'beer'

console.log(x.foo.boo)
console.log(y.foo.boo) // should be 'boo' but it is 'beer'

浅拷贝效果不佳,因为clone的引用指向旧的x对象,而不是克隆的对象。

要解决此问题,并使代码更简洁,可以:

const state = { 1: {a: true, b: true, c: true}, 2: {a: true, b: true, c: true}, 3: {a: true, b: true, c: true}, }
const payload = { 2: {a: true, b: false, c: true} }

const merger = (...args) => _.mergeWith(...args, (a,b) => _.isArray(a) ? b : undefined)

console.log("Merged data:");
console.log(merger(_.cloneDeep(state), payload));
console.log("Manipulated state:");
console.log(state);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

首先切换lodash _.cloneDeep,它将深度复制整个对象树,并且您还可以使ESX merge等使您的spread方法更简洁。