-
Notifications
You must be signed in to change notification settings - Fork 666
Description
I'm hitting an annoyance with memoization of arrays.
I'm keeping normalized data in a redux store as recommended (objects indexed by id, with a separate array of indexes for ordering), and using a selector to denormalize these into an array of objects.
So I'm doing this, effectively:
const selectObjectsById = (state) => state.objectsById;
const selectIds = (state) => state.ids;
const selectDenorm = createSelector(
selectObjectsById,
selectIds,
(objectsById, ids) => ids.map(id => objectsById[id]),
);When state.objectsById changes, selectDenorm will recalculate. But if state.objectsById changes in a way that doesn't change the result of selectDenorm (for example, if a new object is added to state.objectsById whose id isn't in state.ids) then the new array calculated is memberwise identical to the old one but is not reference-equal.
This means a downstream selector is needlessly recalculated. For example:
const selectMain = createSelector(
selectDenorm,
selectFoo,
selectBar,
(denorm, foo, bar) => ({ denorm, foo, bar }),
);I can use createSelectorCreator with a custom equality checker to make a custom selector for selectMain to stop this needless recalculation. But if then selectFoo changes (according to that equality checker), selectMain is recalculated -- using the new selectDenorm result. Which means selectMain(state).denorm is a different reference, even though my custom equality checker insists it's the same.
(At least, this is my understanding of what's going on, which might be wrong.)
If reselect, when it recalculates a selector, uses the previous value of each argument that passed the equality check, then this would improve things. Or perhaps it could run the equality check on the result of the recalculation against the previous result, and return the previous one if they're the same. Or do both?
Or should I be doing things differently?
Thanks for a great library!