What am I trying to do?
Stop re-renders when adjusting the state of an object when I update only one value and the other values remain intact.
What is the code that currently tries to do that?
The front-end component rendering the info:
export default function ReactComponent() {
const [characterState, setCharacterState] = useState(data.character);
useEffect(() => {
socket.emit("/character/read");
socket.on("/character/read", function (data) {
if (data.status === "SUCCESS") {
setCharacterState(data);
}
});
return () => {
socket.off("/character/read");
};
}, []);
return (
<div>
Name: {characterState.name}
Stand: {characterState.stand}
Status: {characterState.status}
</div>
)
}
The back-end .json info as "data" and also what is saved onto characterInfo by setCharacterInfo:
{
status: "SUCCESS"
character: {name: "Dio", stand: "The World", status: "Alive"};
}
What do I expect the result to be?
If the back-end sends something new on the "/character/read", say:
// Status is now "Dead".
{
status: "SUCCESS"
character: {name: "Dio", stand: "The World", status: "Dead"};
}
I expect in the React function to only re-render the status.
What is the actual result?
The whole state is re-rendered (visible to see of the values adjusting), despite the other values in the object not updating.
What I think the problem could be?
I think it has to do with with "useState", that "useState" doesn't care about if there's some changed, all changed, or no changed values, "useState" will always re-render whatever it's given. I'm thinking a possible solution would be making 3 separate states for "characterName", "characterStand", and "characterStatus", but seeking if there's another alternative to the tried attempt of 1 state only.
EDIT:
I failed to relay what I was trying to achieve, but what Sajeeb said was what I was ultimately looking for.
If the attribute, in this case "characterState.status" changes, I expect a re-render on "characterState.status" and not on "characterState.name" nor "characterState.stand". Due to React's default behavior, all of these attributes under 1 state will cause each of them to re-render because the state "characterState" itself was updated.
Thanks for your help everyone.
I would actually recommend you to leave it as is.
Reasoning:
What you now have:
Since your data stored in characterState is so small, it really would not make any sense to break it up or worry about a possible re-render.
In any other case, you'll end up with a less clean and less performant code.
For instance, if you try to break it up into three useStates in one component, you have now 3 states to manage, and you haven't solved your issues with the re-renders.
If you used three different components for the states, you would have:
And less clean code.
All for solving a problem that isn't really a problem
In the case provided by you, I would most certainly recommend leaving it as is.
Unless you have something very specific reason why this would not work well.
You are using 1 state variable which stores a dictionary of 3 values, not 3 different state variables. So when you call the state update function, it will change that 1 state which is a dictionary, instead of changing only a single property of the state variable.
The only solution is to, as you are already onto, create separate state variables.
Cheerio!
Edit: It is to be noticed that despite splitting the state variables, the entire component will still re-render every time a state changes, so 3 different state variables won't prevent entire component refreshing. I suggest you write a different component to display name, stand, status separately so that when the props passed to a particular child component changes (say Status), only that component refreshes.
Edit: I'm still laughing at the JoJo reference lol.