ralusek / reselectie

MIT License
108 stars 6 forks source link

Use memoizeAs inside memoize? #3

Open nickretallack opened 4 years ago

nickretallack commented 4 years ago

Docs say they're composable and shows that you can use a memoize inside a memoizeAs, but can you use a memoizeAs inside a memoize?

I want to do something like this:

import { memoize, memoizeAs } from 'reselectie';

export const objectDistanceSelector = memoizeAs(
    (state, objectId) => calculateDistance(state.objects[objectId].position),
    (state, objectId) => objectId,
)

export const objectsByDistanceSelector = (state) => {
    const objectIds = Array.from(Object.keys(state.objects))
    objectIds.sort((a,b) => objectDistanceSelector(state, a) - objectDistanceSelector(state, b) )
    return objectIds
}

I'm not sure how to memoize the result correctly. I guess it needs to know that it depends on whatever objectDistanceSelector depends on, somehow. Also, if objectDistanceSelector could know not to update for every object when unrelated objects move, that'd be nice.

ralusek commented 4 years ago

@nickretallack your solution should be sufficient. The distance selector is memoized correctly, the handler function objectsByDisntanceSelector will only be recomputed in the event that the state changes.

However, you are calling objectDistanceSelector incorrectly, which should actually be invoked like:

objectDistanceSelector(a)(state);

rather than

objectDistanceSelector(state, a);
nickretallack commented 4 years ago

Do you see the two places where this could be improved though?

objectDistanceSelector will be recomputed if the state changes in any way, but ideally it would only be recomputed if state.objects[objectId].position changed.

Also, I'd like to memoize the sorting that occurs in objectsByDistanceSelector, and only recalculate it if any state.objects[objectId].position changes. It'd be nice if it was somehow aware of what state objectDistanceSelector is depending on.

Multihuntr commented 3 years ago

I came across this problem and saw that it wasn't resolved. My solution was to just wrap reselectie's memoizeAs with my own that matches the same signature as memoize, thus allowing arbitrary composing, just like in reselect.

const myMemoizeAs = (...selectors) => {
    const memAs = memoizeAs(...selectors);
    return (state, key) => memAs(key)(state);
}