Redux, Normalised Entities And Lodash Merge
I'm using Redux, React and Lodash with a fairly standard normalized entities store. When I merge in new entities in a redux reducer, the references to all my existing entities chan
Solution 1:
Use mergeWith
with a customizer:
letkeepRef = (objValue, srcValue) => (
objValue === undefined ? srcValue : _.mergeWith({}, objValue, srcValue, keepRef)
)
let newEntities = _.mergeWith({}, entities, response, keepRef)
Solution 2:
I expanded on @Pavlo's awesome answer. I added support for arrays, and collections. I define a collection as an array of object's, where each object has an id
key. This is very common in react/redux and normalized data.
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;
} elseif (srcValue === undefined) { // do i need this?return objValue;
} elseif (isPlainObject(objValue)) {
return mergeWith({}, objValue, srcValue, keepUnchangedRefsOnly);
} elseif (Array.isArray(objValue)) {
if (isEmpty(objValue) && !isEmpty(srcValue))return [...srcValue];
elseif (!isEmpty(objValue) && isEmpty(srcValue)) return objValue;
elseif (isEmpty(objValue) && isEmpty(srcValue)) return objValue; // both emptyelse {
// 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 spotif (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 ];
}
}
}
}
Usage:
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)
Resulting stateNext
is:
{
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'
}
]
}
If you want to keep undefined
values, then replace mergeWith
in customizer and your use case with assignWith
. Example - https://stackoverflow.com/a/49455981/1828637
Post a Comment for "Redux, Normalised Entities And Lodash Merge"