I have a list of entities
, each with the property active
on it. I want to select those that are active, and to avoid rerendering them each time entities
is updated (as an entity which is not active may be added). I figured I could keep track of activeIds
, and update it when an entity
becomes active.
My issue is, to grab the active entities, I would guess to write something like:
const selectActiveEntities = createSelector(
(state) => state.entities.items,
(state) => state.entities.activeIds,
(entities, activeIds) => activeIds.map(id => entities[id])
)
However, this creates an issue! I believe it will re-render whenever state.entities.items
is updated. I, the programmer can identify that the output won't change unless state.entities.activeIds
explicitly changes. How do I achieve dependence solely on state.entities.activeIds
?
I believe it will re-render whenever
state.entities.items
is updated.
Yes, kind of. But don't think of these a "re-renders" here just yet though, think of these as inputs and outputs. When any input to a selector function updates its value, it will trigger the selector to re-compute its output value. This should be expected and desired behavior.
I, the programmer can identify that the output won't change unless
state.entities.activeIds
explicitly changes.
You might know this, but the selector function can't. All it has to go by are the inputs. This is also simply not true. Either of the input values can change. As stated above, any time any input value changes, the selector will re-compute its output. It doesn't know yet that just because only the active ids changed that the output will be the same... it just recomputes and memoizes the output value, which just might happen to be the same as the previous output.
How do I achieve dependence solely on
state.entities.activeIds
?
You can't really, since you are computing a derived state that depends on both state.entities.activeIds
and state.entities.items
, both values are used as inputs for the output you are computing.
const selectActiveEntities = createSelector(
[
(state) => state.entities.items, // <-- items input
(state) => state.entities.activeIds, // <-- activeIds input
],
(items, activeIds) => activeIds.map( // --> filtered activeIds output
id => items[id]
)
);
If you like, you can reduce the code a bit and use state.entities
as the input value, but this then depends on the entire state.entities
object value instead of just the activeIds
and items
values.
const selectActiveEntities = createSelector(
[(state) => state.entities],
(entities) => entities.activeIds.map(id => entities.items[id])
);
Because you are working with arrays, and as was suggested in your other post regarding a similar question around selectors and component rerendering, if the UI components selecting these active entities still needs a boost you should use the shallowEqual
function.
import { shallowEqual, useSelector } from 'react-redux';
...
const activeEntities = useSelector(selectActiveEntities, shallowEqual);