-
Notifications
You must be signed in to change notification settings - Fork 666
Description
Currently, we have this problem with selectors which derive and return arrays, whereby the array reference is not preserved even if the array contents have not changed:
const assert = require('assert');
const { createSelector } = require('reselect');
const state1 = {
users: {
foo: { id: 'foo' },
bar: { id: 'bar' },
},
};
const state2 = {
users: {
foo: { id: 'foo', age: 1 },
bar: { id: 'bar' },
},
};
const getUsersById = state => state.users;
const getUserIds = createSelector(getUsersById, users => Object.keys(users));
// error!
// they are deep equal but not reference equal
assert(getUserIds(state1) === getUserIds(state2));We would like to write our selectors so that the array reference is preserved if the contents of the array have not changed.
As far as I understand, selectors like this (which derive and return arrays) are very common in React Redux apps. In React Redux, the best practice is for list components to pass IDs down to each child, and for the list item child to connect and lookup its own state value using the provided ID. The reason this is good for performance is because then the parent list component (and all of its children) will not have to re-render when one of the items changes. (More information in this article).
However, this performance optimisation only works if the selector that returns an array of IDs is correctly memoized—and currently there's just no straightforward way to do that with reselect, out of the box.
(In this case, we could store an array of IDs in Redux, as well as the dictionary, but that wouldn't work in many other cases e.g. where we're filtering an array inside a selector.)
Given this is a common pattern, I believe reselect should provide a direct solution, out of the box.
Related discussions: