'State isn't updated in a function if that function is called outside of the hook
I have an issue and i don't know how to solve it.
Basically, the idea is that i have a class that i need to instanciate only once (instanciation is done in an useMemo in order to have it done only once), this class is in reality a websocket implementation which calls a handler (here onCounterChanged) when it receives a message from the backend), in order to update a component state, but for some reason this handler holds the old counter state value. Here is the code :
class MyService {
constructor(onCounterChange) {
this.onCounterChange = onCounterChange;
}
start = () => {
//This setInterval is only used to try to reproduce the reality (an async event so for example
//when a message is received from the server). So i call this onCounterChange every 2 sec to
//simulate a message received from the server
setInterval(() => {
this.onCounterChange();
}, 2000);
};
}
const MyComponent = () => {
const [counter, setCounter] = React.useState(0);
const onCounterChanged = () => {
console.log("onCounterChanged", counter);
setCounter(counter + 1);
};
//i'm using useMemo in order to keep the instance of MyService, because i'm doing some stuff
//(connect to the webSocket etc... in this class, i don't want to create an new instance on every
//render and do the connection to the server again
const myService = React.useMemo(() => new MyService(onCounterChanged), []);
React.useEffect(() => {
myService.start();
}, []);
return <div>{counter}</div>;
};
Here is a sandbox to see what's happening : https://codesandbox.io/s/blazing-tree-z1m99e?file=/src/App.js
You can see that the counter isn't incremented, the function onCounterChange is called every seconds but the counter state is not updated inside of that handler. I tried to fix it by using useCallback but nothing was working.
Any idea how can i fix this ? I'm not a pro of react so, sorry if i'm missing something.
Thanks for reading.
Solution 1:[1]
There is an issue with your update state, I did a little refactor.
When you try to update the state using the old value, you should use a callback instead of passing the "current" value, avoid passing the current state value because is not always in sync with the actual state value
const onCounterChanged = useCallback(() => {
setCounter((currentValue) => {
console.log({ currentValue });
return currentValue + 1;
});
}, [setCounter]);
Using a callback will assure you that is using the latest value
Here is a CodeSandbox: https://codesandbox.io/s/goofy-sanderson-bfq3mh?file=/src/App.js
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Link Strifer |
