I have a store called stories
which has a userStories
object stored.
Example of userStories
object inside store:
userStories: {
userId1: [{storyId: 1, viewedBy: [id5, id2, id9]}, {storyId: 2, viewedBy: [id1, id2, id3]}]
userId2: [{storyId: 6, viewedBy: [id25, id42, id29]}, {storyId: 7, viewedBy: [id21, id22, id33]}]
}
I subscribe to the stories for a particular userId
in my Home component like so:
const stories = useSelector((state) => state.stories.userStories[userId])
When a user clicks a button in my component, I dispatch a method called updateStories
which takes an array
of story ids as well as a user id. It should update all story objects for a particular user id.
Here is the reducer:
updateStoryViewedBy: (state, action) => {
const { storyIds, storyCreatorId} = action.payload
const stories = state.userStories[storyCreatorId]
// loop over all story ids => find them in users stories => update them each individually
storyIds.forEach((id) => {
const s = stories?.find((s) => s?._id === id)
s.isSeen = true
})
},
Here I am directly mutating the state. If storyIds
contains n
id’s, then the store is directly mutated n times, once for each object matching the story id. As each direct mutation to a store causes a re-render to a component that subscribes to that store, this would in theory update the Home
component n
times as it is using useSelector
to subscribe to the store. However my Home
component isn’t re-rendering n
times.
Why is this, and is using a loop in a reducer method to update individual objects in an array considered problematic in redux-tookit
?
Could it be more beneficial to perhaps use state.userStories[userId] = state.userStories[userId].map(...)
to only update the store once and avoid mutating? Thanks
1
Here I am directly mutating the state. If storyIds contains n id’s,
then the store is directly mutated n times, once for each object
matching the story id. As each direct mutation to a store causes a
re-render to a component that subscribes to that store, this would in
theory update the Home component n times as it is using useSelector to
subscribe to the store. However my Home component isn’t re-rendering n
times.
I believe the assumption of each mutation causing a re-render does not happen here. The shallow equality checking may not be seeing each change because you are directly mutating the state, which you generally shouldn’t be doing. I think this should be a better way of writing the reducer to create a new array instead of updating the current one. Not too sure about the logic here so let me know if you run into any issues with this
updateStoryViewedBy: (state, action) => {
const { storyIds, storyCreatorId } = action.payload;
const stories = state.userStories[storyCreatorId];
if (stories) {
const updatedStories = stories.map(story => {
if (storyIds.includes(story._id)) {
return { ...story, isSeen: true };
}
return story;
});
state.userStories[storyCreatorId] = updatedStories;
}
}
justnut is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1