我扩展了@Pavlo 的精彩答案。我添加了对数组和集合的支持。我将一个集合定义为一个对象数组,其中每个对象都有一个id
键。这在 react/redux 和规范化数据中很常见。
import { mergeWith, isPlainObject, isEmpty, keyBy } from 'lodash'
// https://stackoverflow.com/a/49437903/1828637
// mergeWith customizer.
// by default mergeWith keeps refs to everything,
// this customizer makes it so that ref is only kept if unchanged
// and a shallow copy is made if changed. this shallow copy continues deeply.
// supports arrays of collections (by id).
function keepUnchangedRefsOnly(objValue, srcValue) {
if (objValue === undefined) { // do i need this?
return srcValue;
} else if (srcValue === undefined) { // do i need this?
return objValue;
} else if (isPlainObject(objValue)) {
return mergeWith({}, objValue, srcValue, keepUnchangedRefsOnly);
} else if (Array.isArray(objValue)) {
if (isEmpty(objValue) && !isEmpty(srcValue))return [...srcValue];
else if (!isEmpty(objValue) && isEmpty(srcValue)) return objValue;
else if (isEmpty(objValue) && isEmpty(srcValue)) return objValue; // both empty
else {
// if array is array of objects, then assume each object has id, and merge based on id
// so create new array, based objValue. id should match in each spot
if (isPlainObject(objValue[0]) && objValue[0].hasOwnProperty('id')) {
const srcCollection = keyBy(srcValue, 'id');
const aligned = objValue.map(el => {
const { id } = el;
if (srcCollection.hasOwnProperty(id)) {
const srcEl = srcCollection[id];
delete srcCollection[id];
return mergeWith({}, el, srcEl, keepUnchangedRefsOnly);
} else {
return el;
}
});
aligned.push(...Object.values(srcCollection));
return aligned;
} else {
return [ ...objValue, ...srcValue ];
}
}
}
}
用法:
const state = {
chars: ['a', 'b'],
messages: [
{
id: 1,
text: 'one'
},
{
id: 2,
text: 'ref to this entry will be unchanged'
}
]
}
const response = {
chars: ['c', 'd'],
messages: [
{
id: 1,
text: 'changed ref text one'
},
{
id: 3,
text: 'three'
}
]
}
const stateNext = mergeWith({}, state, response, keepUnchangedRefsOnly)
结果stateNext
是:
{
chars: [
'a',
'b',
'c',
'd'
],
messages: [
{
id: 1,
text: 'changed ref text one'
},
{
'id': 2,
text: 'ref to this entry will be unchanged'
},
{
'id': 3,
text: 'three'
}
]
}
如果您想保留undefined
值,请将mergeWith
自定义程序和您的用例替换为assignWith
. 示例 - https://stackoverflow.com/a/49455981/1828637