I'm creating an app with React and I'm struggeling with state variables. An example:
import { useEffect, useState } from "react";
export default function App() {
const [a, setA] = useState(["inital"]);
useEffect(() => {
setTimeout(() => {
setA(["hello"]);
}, 1000);
setTimeout(() => {
setA([...a, " world"]);
}, 2000);
}, []);
return (
<div>{a}</div>
);
}
Mutating State Functional Component Forked
The result in the view is:
0 sec: initial
1 sec: hello
2 sec: initial world
What I expected:
0 sec: initial
1 sec: hello
2 sec: hello world
It's confusing because the state hello
was definitely set like you can see in the view. Why does a
has the value initial
in the second timeout?
CodePudding user response:
setA([...a, " world"]);
is taking the value at the time when you create your function (before the wait, so before the first setTimeout
callback is called). At this time the value is "initial"
If you want it to update properly you could use
setA(prev => [...prev, " world"]);
This will get the value of the state when the setA
function is actually called and give you the correct result.
CodePudding user response:
This is related to closure -
At the time of creation, the line setA([...a, " world"])
still refers to the initial a
.
You can solve this by a second useEffect
which will have a
as a dependency, but then you'll probably run into an infinite loop, so you might have to include a condition.
useEffect(() => {
setTimeout(() => {
setA(["hello"]);
}, 1000);
}, []);
useEffect(() => {
if (a.length === 1) {
setTimeout(() => {
setA([...a, " world"]);
}, 2000);
}
}, [a]);
}, []);