const TestApp = () => {
const [speed, setSpeed] = useState(1);
// a state updated in oanother function, but below function can not get new state
const incrementSpeed = () => {
console.log(speed, `speed`); // Always console 1, how can I get new state?
window.requestAnimationFrame(incrementSpeed);
};
useEffect(() => {
const handle = (e) => {
if (e.key === 'o') {
setSpeed(() => speed + 1);
} else if (e.key === 'p') {
incrementSpeed();
}
};
window.addEventListener('keydown', handle);
return () => window.removeEventListener('keydown', handle);
}, [speed]);
return <></>;
};
export default TestApp;
Guys, I got a problem, I have updated my code.
4
Answers
If I’m not mistaken, what you’re looking for is a way to properly increment the value. Here’s how I would go about it.
React state updates especially on functions with hooks are going to be batched, so whenever you
console.log
in your code, you will see the current value of variablespeed
. It is expected as it hasn’t run through the full cycle of re-render/state update.To ensure that increment is done properly based on what the current value is, you can pass a function into the
setter
function that does exactly that.Your incrementSpeed function would look like below:
You may also log it inside that function like below, but I’m not sure why you would want that in the current context but here it is:
speed
is a closed in variable. Think closures. The value ofspeed
was closed over byincrementSpeed
. It is logging and using the same value.See, the
incrementSpeed
which is created afresh with the new state value is not the one passed intorequestAnimationFrame
.requestAnimationFrame
is still using only one version ofincrementSpeed
which has the value of speed as 1.With hooks based React, the best place to see updated state values is inside the render body itself. There you can see the updated values.
So move
console.log
at the same level asincrementSpeed
.But the problem that the
speed
reference is closed over still exists. So you will have to use the updater function pattern ofsetState
. This guarantees that you get the previous state value correctly and is the recommended way.Demo
NOTE: While
console.log
inside the setter can be done to check the value, it is not recommended as it is a side effect.In React Functional components when you update the state you can’t get the latest values of State in function. when you call the function next time you will get the last updated value.
For example:
keydown 1st time:
log: 1 speed
speed value of state after update: 2
keydown 2nd time:
log: 2 speed
speed value of state after update: 3
To get the latest value in console log:
Or like this:
As per this answer here https://stackoverflow.com/a/67244182/11976596
A better approach would be to pass updated state as parameter as below