Can shallowEqual be avoided?
Noitidart opened this issue · 5 comments
In this case here, I'm not able to avoid shallowEqual.
Here are my states:
const state0 = { winners: [] };
const state1 = { winners: [{ name: 'Bar' }] };
const state2 = { winners: [{ name: 'Bar' }, { name: 'Foo' }] };
const state3 = { winners: [{ name: 'Bar' }, { name: 'Foo' }, { name: 'Foo' }] };
We see in state0
we have empty array.
In state1
we add Bar.
In state2
we add Foo.
In state3
we add Foo again.
Here are my selectors:
const selectWinnerValues = state => {
console.log('selecting winner values');
return state.winners;
};
const selectUniqueNames = memoize(winnerValues => {
console.log('selecting unique names');
// this here de-dupes the names
return [...new Set(winnerValues.map(v => v.name))];
});
Now running the selectors on the states:
const return0 = calcWinnerNames(selectWinnerValues(state0));
const return1 = calcWinnerNames(selectWinnerValues(state1));
const return2 = calcWinnerNames(selectWinnerValues(state2));
const return3 = calcWinnerNames(selectWinnerValues(state3));
I will now log out values:
// { return0: [], return1: ['Bar'], isEqual: false }
console.log(JSON.stringify({ return0, return1, isEqual: return0 === return1 }));
// { return1: ['Bar'], return2: ['Bar', 'Foo'], isEqual: false }
console.log(JSON.stringify({ return1, return2, isEqual: return1 === return2 }));
// this last one is unexpected, it should be the same value
// { return2: ['Bar', 'Foo'], return3: ['Bar', 'Foo'], isEqual: false }
console.log(JSON.stringify({ return2, return3, isEqual: return2 === return3 }));
We see return2
and return3
are both ['Bar', 'Foo']
. However they are referentially different.
Is there a way in this case, to get referentially identical values when both string values are same?
Is there a way in this case, to get referentially identical values when both string values are same?
I don't think so. So, what this lib does is to track the property access and if any of the accessed property value is changed, it will re-compute.
We can create a much simpler example.
const fn = memoize(x => ({ sum: x.a + x.b }));
console.log(fn({ a: 1, b: 2 }) === fn({ a: 1, b: 2 })); // true
console.log(fn({ a: 1, b: 2 }) === fn({ a: 2, b: 1 })); // false
So, again the mental model can be different. In this case, we'd need custom equality function to compare the results. Not sure if we call it a limitation or a nature.
Oh no problem. So I think I'll just have to always to useSelector
with the 2nd argument always being shallowEqual
.
Thanks!
we should always do shallowEqual with useSelector
I think not. It should require shallowEqual only if it's the problem.
We have various options to resolve it like returning a primitive.
Furthermore, if it's two-level deep, shallowEqual doesn't work anyway.
So, it really depends. I would guess it's a rare case to require equalityFn, so if I were to add a note, it would be described as a exceptional note (after other better options maybe).
Thanks for your deep consideration.