I have the following code:
import { atom, atomFamily, selector, selectorFamily } from 'recoil';
import { VIDEO_STATUSES } from '../../constants';
export const videosDict = atomFamily({
key: 'videos',
default: {},
});
export const videoIds = atom({
key: 'videoIds',
default: [],
});
export const videosFilteredState = atom({
key: 'videosFilteredState',
default: VIDEO_STATUSES.SCRAPED.value,
});
export const videosFiltered = selector({
key: 'videosFiltered',
get: ({ get }) => {
const filter = get(videosFilteredState);
const ids = get(videoIds);
switch (filter) {
case VIDEO_STATUSES.SCRAPED.value:
return ids.filter((id) =>
[
VIDEO_STATUSES.SCRAPED.value,
VIDEO_STATUSES.APPROVED.value,
].includes(get(videosDict(id)).status),
);
case VIDEO_STATUSES.UPLOADED.value:
return ids.filter(
(id) => get(videosDict(id)).status === VIDEO_STATUSES.UPLOADED.value,
);
case VIDEO_STATUSES.HIDDEN.value:
return ids.filter(
(id) => get(videosDict(id)).status === VIDEO_STATUSES.HIDDEN.value,
);
default:
return ids;
}
},
});
export const videosSelector = selectorFamily({
key: 'videosSelector',
get:
(id) =>
({ get }) =>
get(videosDict(id)),
set:
(id) =>
({ set }, data) =>
set(videosDict(id), data),
});
This is working as expected, the list of ID's is used in the parent list component
where it will use a .map
and render children item
components based on the list ID's
Within the item
components, it will use the videosSelector
to grab the actual data so that I can have a local state.
When I click a button within the item
, it re-renders the entire list (shown through React Dev Tools highlighting).
If I hack it up and just use videoIds
instead of videosFiltered
, it doesn't re-render the whole list.
In the list component, I get the filtered videos like so:
const videos = useRecoilValue(videosFiltered);
...
return <Grid container>
{videos.map((id) => (
<Video key={id} id={id} />
))}
</Grid>
Why does the list re-render when I'm updating the state of a single member of the atomFamily?