Javascript 还原、标准化实体和lodash合并

Javascript 还原、标准化实体和lodash合并,javascript,reactjs,redux,lodash,Javascript,Reactjs,Redux,Lodash,我正在使用Redux、React和Lodash以及一个相当标准的规范化实体存储 当我在redux reducer中合并新实体时,对所有现有实体的引用都会更改(尽管没有被修改),从而导致任何纯组件重新渲染 是否有替代lodash合并的方法,可以在合并的同时保留对不在合并对象中的值的现有引用 let entities = { [1]: {a: true }, [2]: {a: true, b: true }, } let response = { [2]: {a: false } }

我正在使用Redux、React和Lodash以及一个相当标准的规范化实体存储

当我在redux reducer中合并新实体时,对所有现有实体的引用都会更改(尽管没有被修改),从而导致任何纯组件重新渲染

是否有替代lodash合并的方法,可以在合并的同时保留对不在合并对象中的值的现有引用

let entities = { 
  [1]: {a: true },
  [2]: {a: true, b: true },
}
let response = { 
  [2]: {a: false }
}
let newEntities = _.merge({}, entities, response)

console.log(entities[1] === newEntities[1]) // false
我无法在此处使用Object.assign/ES6,因为
newEntities[2].b
将被删除

我确实意识到有其他解决方案,如定制sCU和重新选择,但是在减速器级别处理这一问题比修改每个对其道具进行平等参考检查的组件要干净得多。

与定制者一起使用:

let keepRef = (objValue, srcValue) => (
  objValue === undefined ? srcValue : _.mergeWith({}, objValue, srcValue, keepRef)
)
let newEntities = _.mergeWith({}, entities, response, keepRef)

我进一步阐述了@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'
        }
    ]
}

如果要保留未定义的
,请将customizer中的
合并为
,并将您的用例替换为
赋值为
。示例-

我们是否需要测试objValue是否未定义?如果是,默认机制不会返回srcValue吗?反之亦然(objValue未定义,但srcValue未定义)?请查看我的解决方案,该解决方案支持数组和“按id收集”的数组-