useState hook setter incorrectly overwrites state
Welcome to the closure hell. This issue happens because whenever setState
is called, state
gets a new memory reference, but the functions changeValue1
and changeValue2
, because of closure, keep the old initial state
reference.
A solution to ensure the setState
from changeValue1
and changeValue2
gets the latest state is by using a callback (having the previous state as a parameter):
import React, { useState } from "react";
const Test = () => {
const [state, setState] = useState({
value1: "1",
value2: "2"
});
const changeValue1 = () => {
setState((prevState) => ({ ...prevState, value1: "new 1" }));
};
const changeValue2 = () => {
setState((prevState) => ({ ...prevState, value2: "new 2" }));
};
// ...
};
You can find a broader discussion about this closure issue here and here.
Your functions should be like this:
const changeValue1 = () => {
setState((prevState) => ({ ...prevState, value1: "new 1" }));
};
const changeValue2 = () => {
setState((prevState) => ({ ...prevState, value2: "new 2" }));
};
Thus you make sure you are not missing any existing property in the current state by using the previous state when the action is fired. Also thus you avoid to have to manage closures.