Skip to content

Dependency recalculation with arrays #74

@avaragado

Description

@avaragado

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions