Asaf
Asaf

Reputation: 2168

Redux Reselect - selector with argument input is recalculating

I have the following selector:

const getAllAddresses = (withStartEnd) => 
    createSelector(
        [getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
        (all, startAddress, endAddress) => {
            if (!withStartEnd) return [...Object.values(all)];
            return [startAddress, ...Object.values(all), endAddress];
        }
    );

I noticed that the selector is re-calculating every time, event when all, startAddress and endAddress do not change. If I remove the input for the selector function, to something like this:

const getAllAddresses = (
    createSelector(
        [getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
        (all, startAddress, endAddress) => {
            return [startAddress, ...Object.values(all), endAddress];
        }
    )
);

Then everything works as expected and the selector does not re-calculate on every call. Seems like I missing something in the selector concept. Any help would be much appreciated.

Upvotes: 3

Views: 7704

Answers (1)

Jordan Enev
Jordan Enev

Reputation: 18664

Update:

Please refer to How do I create a selector that takes an argument?

In short: the way you did it, will work only if you pass static arguments and create a factory function outside of mapStateToProps. For dynamic arguments it's more complex and please follow the resource I already mentioned above.

The reason your selector is recalculated each time mapStateToProps is called is that calling getAllAddresses will create a new instance of createSelector and the memoization won't work.


Original answer:

In short, reselect determines input selector changes, based on identity check ===.

Therefore, if your input selectors always create and return a new object or array, then your selector will be recalculated each time.

In order to fix the recalculation issues:

  1. Make sure your input selectors always return a reference, instead of new object / array.
  2. Or, if a new object / array is the proper returned value, then you have to customize the equalityCheck for defaultMemoize

From the reselect docs:

Why is my selector recomputing when the input state stays the same? (please follow the link, there are great examples)

Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using Redux). For example, a selector created with createSelector that recomputes unexpectedly may be receiving a new object on each update whether the values it contains have changed or not. createSelector uses an identity check (===) to detect that an input has changed, so returning a new object on each update means that the selector will recompute on each update.

Upvotes: 3

Related Questions