There are three type of selectors:
- basic
- cached
- cached factory
// Basic
const selectUsers = state => get(state, '_entites.users');
const selectUserById = (state, { id }) => get(state, ['_entites', 'users', id]);
// Cached
const selectActiveUsers = createSelector(
selectUsers, // this cached selector depends only on state
users => pickBy(users, u => u.active)
);
// Cached Factory
const makeSelectUsersFromCity = () => createSelector(
selectUsers,
(_, { city }) => city, // this cached selector depends on a prop, not just state
(users, city) => pickBy(users, u => u.city === city);
);
Basic selectors are selectors that only need to select a value from the state. Basic selectors do not derive data from the state by mapping, reducing, or filtering.
The fastest way for a selector to only pull values from the state is to just pull it out of the state:
const selectUsers = state => get(state, '_entites.users');
const selectUserById = (state, { id }) => get(state, ['_entites', 'users', id]);
Notice that in these selectors, adding a cache will only add complexity and time, since you'll need to select from the state in order to check the cache.
Cached selectors are selectors that need to derive some value from the state only. Frequently these selectors will map, filter, or reduce over some slice of the state, checking for the values that it needs.
const selectActiveUsers = createSelector(
selectUsers, // this cached selector depends only on state
users => pickBy(users, u => u.active)
);
In this example, we're mapping over all of the users in the state to find the active users. We cache this selector with createSelector
so that we avoid the map if the users have not changed.
Cached factory selectors are selectors that need to derive some value from the state based on props. Just like cached selectors, these selectors will map, filter, or reduce over some slice of the state, checking for the values it needs. But unlike a cached selectors, a cached factory selector depends on props--not only raw state. This means that cached factory selectors need a unique cache for the props that are passed in.
A facotry of cached selectors allows a new cache to be created for the selector when it's used.
// Cached Factory Selector
const makeSelectUsersFromCity = () => createSelector(
selectUsers,
(_, { city }) => city, // when a different city is passed in, the cache is broken
(users, city) => pickBy(users, u => u.city === city);
);
Here, we need to have a unique cache for every city that is passed in.
To explain further, consider using this selector without a factory:
const akronUsers = selectUsersFromCity(state, { city: 'akron' });
const newYorkUsers = selectUsersFromCity(state, { city: 'nyc' });
const akronUsers2 = selectUsersFromCity(state, { city: 'akron' });
With this usage, the cache is broken when selecting nyc
. So when selecting akron
again with the same state and props as the first line, we break the cache and re-compute those values.
To fix this, we use a factory to create a selector instance for every city:
const selectUsersFromCity1 = makeSelectUsersFromCity();
const selectUsersFromCity2 = makeSelectUsersFromCity();
const akronUsers = selectUsersFromCity1(state, { city: 'akron' });
const newYorkUsers = selectUsersFromCity2(state, { city: 'nyc' });
const akronUsers2 = selectUsersFromCity1(state, { city: 'akron' });
Here, nyc doesn't break the cache for akron. This example maps similarly to react components that are connected and create their own selector instance.