I encountered this behavior. Two onChange are called in turn. One of them saves data to the state, and use the previus state to generate new one.
const onChange = (data) => {
setState1((prev) => {
console.log("onChange set state");
return [...prev, data];
});
};
The other just prints something to the console.
const onChange2 = (data) => {
console.log("onChange2");
};
onChange and onChange2 called after click on button
<button
onClick={() => {
onChange({ foo: "bar" });
onChange2();
}}
>
2
</button>
useEffect - saves data to state
const [state, setState] = useState(0);
const [state1, setState1] = useState([]);
useEffect(() => {
setState((prev) => {
console.log("use effect set state");
return prev 1;
});
}, []);
So, if you do not use useEffect (comment out), the console log, which in the first onChange will be called first, then the console log from the second onChange
If you uncomment useEffect, then the console will log from onChange2 first, then from onChange
Why is this happening, who can tell?
CodeSandBox: https://codesandbox.io/s/eager-hooks-sczvb
CodePudding user response:
The call order of onChange
and onChange2
appear to change because the console.log
in onChange
is inside the callback passed to setState1
.
If you move the console.log
directly in the onChange
you will see that the order is the same with or without useEffect
.
const onChange = (data) => {
console.log("onChange set state");
setState1((prev) => {
return [...prev, data];
});
};
It's important to understand that when you call setState
there is no guarantee that the state will be changed immediately, it's up to React to decide when to apply the new state and trigger a new render.
This is why the callback passed to setState should only return a new state and not run any effects (such as console.log
) since the moment this code is run is considered in implementation detail and could change with any version of React.
CodePudding user response:
Expected behavior is onChange2
called first, and onChange set state
called second whatever useEffect body is commented. because onChange2
called right now. but onChange set state
in setState fn called after rendered。
The "bug" looked is why onChange set state
called first when first click. Hmm... find react computeExpirationForFiber. setState callback execute sync or async by different conditions. It's hard to read.