I'm working on a React app with websocket. In this app there is a socket event listener which returns 3 types: add
, change
, and remove
. Certain state update operations will be performed based on the type received.
Do I properly mutate the users
state? I'm not sure whether it follows the principle of React regarding updating states.
ws.on("connect", () => {
console.log("connected");
ws.emit("load");
});
ws.on("feed", response => {
const prev = response.old_val;
const next = response.new_val;
const type = response.type;
setUsers(users => {
let currentUser = [...users];
let index;
switch (type) {
case "add":
return [...users, next];
case "change":
index = users.findIndex(u => u.id === next.id);
currentUser[index].name = next.name;
return currentUser;
case "remove":
index = users.findIndex(u => u.id === prev.id);
currentUser.splice(index, 1);
return currentUser;
default:
return currentUser;
}
});
});
1 Answer 1
This seems reasonable. The most important part is that you do not mutate the react state in-place. By doing let currentUser = [...users];
you copy the state and instead mutate the copy, then assign it as the updated state.
An alternative would be to use .map
and .filter
to make your changes. I see this pattern more often than the state copy approach.
Example:
ws.on("feed", response => {
const { new_val: next, type } = response; // Use destructuring and inline renaming.
setUsers(users => {
switch (type) {
case "add":
return [...users, next];
case "change":
return users.map(u => {
// Only change the user specified in the message
if (u.id === next.id) return {...u, name: next.name }
return u;
// or return users.map(u => u.id !== next.id ? u : ({...u, name: next.name})) if you prefer one-liners
})
case "remove":
return users.filter(u => u.id !== next.id);
default:
return users;
}
});
});