Nested Object Arrays Do Not Deep Equal Compare
SlowBurner opened this issue · comments
Hi, thank you for a great lib.
It seems weird that you can not track a nested object inside of an array without explicitly accessing each property you want to track.
Here is what I mean. The two selectors below give very different results, and that seems unexpected.
Here is a codesandbox to show how differently they behave.
https://codesandbox.io/s/proxy-memoize-nested-object-array-v9zw5m?file=/src/App.js:4033-4204
const initialState = {
visit: {
id: 1,
name: "Visit 1",
work_areas: [
{
name: "Visit - Work Area 1",
lineItems: [{ item_id: 1, name: "Line Item 1" }],
custom_input_values: [
{ id: 1, name: "Test CI 1", value: "Test CI 1 value" }
]
},
{
name: "Visit - Work Area 2",
lineItems: [{ item_id: 2, name: "Line Item 2" }],
custom_input_values: [
{ id: 2, name: "Test CI 2", value: "Test CI 2 value" }
]
}
]
}
};
// Stops unwanted re-rendering
// This will not recalculate and will return the cached object, when visit.work_areas[0].name changes.
// This what I want it to do.
const getLineItemsTracked = memoize(({ state, index }) => {
const workAreaState = state.visit.work_areas[index];
return {
lineItems: workAreaState.lineItems.map((lineItem) => ({
item_id: lineItem.item_id,
name: lineItem.name
}))
};
});
// Causes unwanted re-rendering
// This will recalculate and always return a new reference, when visit.work_areas[0].name changes.
// This seems like it should do a deep equals on visit.work_areas[0].lineItems, but it does not.
const getLineItemsNotTracked = memoize(({ state, index }) => {
const workAreaState = state.visit.work_areas[index];
return {
lineItems: workAreaState.lineItems
};
});
// I call the selectors in a Functional components like this:
const { values } = useSelector(
useCallback(
memoize((state) => ({
values: getLineItemsTracked({ state, index })
})),
[index]
)
);
const { values } = useSelector(
useCallback(
memoize((state) => ({
values: getLineItemsNotTracked({ state, index })
})),
[index]
)
);
In the real world, each object inside the lineItems array has 40+ properties.
It does not seem practical to have to list out 40+ properties.
Is there anyway to say track nested properties so a deep equals is done on the lineItems array?
I did write a function to access all nested values (Object or Array) if trackNested is true.
I would rather not use a deepEquals in the useSelector call.
Is this by design or am I doing something wrong?
Thanks!
you can not track a nested object inside of an array without explicitly accessing each property you want to track.
If you don't access a property, it's not tracked. If no properties are accessed, the entire object is tracked, but it's rare that it happens.
I had hard time understanding your example. Could you please create a much smaller example? It doesn't need to be realistic. I wonder if "arrays" are important here.
Sorry for delay. What you are saying makes sense. I need to think about more and I have not had anytime to do so.
I think I am just saying it would be nice to have an optional param to say "Track Everything nested inside this Array or Object".
I have had to use a function I wrote several times in our code which traverses the object or array to trigger access on nested values, thus a deep equals comparison. If the object path you access ends on an array or object, and trackNested = true, then you could say all nested values are tracked, instead of having to explicitly access them in your code.
"Track Everything nested inside this Array or Object"
It's technically possible and required for some cases,
but it's pretty difficult to explain and it would only confuse people.
Line 5 in 44c7979
So, it's not exported. You can try it by importing directly from proxy-compare
.