Open emroot opened 8 years ago
Ok, first things first. You should use the latest beta and take it from there.
Secondly, if you deepmerge a normalized entity you need to avoid that the merger function tries to deep merge a proxy reference (e.g. you have a structure like so:
posts:{
"1 :{
id:1
group: Proxy(15)
}
},
groups:{
"15":{
label: "bla",
posts:[Proxy(1)]
}
}
Trying to deepmerge a proxy fails because you will "lose" your Record definition in the process and it gets turned into a Map and you will get all sort of other "strange errors".
So, what I do to avoid this is use a little property I added to the Proxy in the latest beta and I use this as a merge function:
/**
* merger - Used as a merger function for deep merging immutable objects
* If a collection is encountered, it will replace the old collection with the new collection because default behaviour is just adding
* If a proxy is encountered it will replace the old proxy with the new proxy
*
* This means that in both cases the references change but the content may be the same
*
* @param {type} a description
* @param {type} b description
* @return {type} description
*/
export function merger(a, b) {
if (a && a.mergeWith && !isList(a) && !isList(b) && !isSet(a) && !isSet(b) && !a._isProxy) {
return a.mergeDeepWith(merger, b)
}
return b
}
Thirdly, if you merge proxied entities and you "touch" the proxy before it is merged into the entity structure, e.g. sorting, you will get an error, because you will touch properties through the proxy that are not in the store yet. So, I merge my entities first and then proceed to process the result. Like so:
/* action code */
const json = await response.json();
const normalized = normalizeJson(json.posts.items, arrayOf(postSchema), getState);
dispatch(shared.actionCreatorForMerge(REDUCER)(normalized.entities));
dispatch(processPosts({
result:normalized.result,
resultCount:json.posts.resultCount
}));
/* shared reducer */
export function mergeEntitiesState(reducerState, payload){
return reducerState.set('entities',reducerState.get('entities').withMutations(state => {
//we need to make sure the root keys in the payload are there
payload.forEach((mp, schema) => {
if(!state.has(schema))
state = state.set(schema, new Map());
mp.forEach((entity, id) => {
//Bug https://github.com/facebook/immutable-js/issues/975 entity gets converted to Record with mergeIn. setIn works
state = state.setIn([schema, id],entity);
});
});
return state;
})
);
// set('entities',reducerState.get('entities').mergeDeepWith(merger, payload));
}
export function actionTypeForMerge(name){
return name.toUpperCase() + '_MERGE_ENTITIES';
}
export function actionCreatorForMerge(name){
return function mergeEntities(payload){
return {
type: actionTypeForMerge(name),
payload
}
}
}
/* reducer */
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case shared.actionTypeForMerge(REDUCER):
return shared.mergeEntitiesState(state, action.payload);
[...]
HTH
Hey I'm having some trouble merge entities together, I might be doing something wrong, so wanted to get some feedback here first. My view loads an initial list with basic information, the view is paginated, when I scroll down, we fetch more items from the backend, which should merge the data to the current state. When I click on an individual item, I will show the basic information, fetch more data in the background for this specific item and merge it to the entities. I'm having issue with the merge, when I try to mergeDeep I get
collection.update
error. Here's a sample of my code below: