I have a JS library (Cytoscape) inside my React component. The library need to be handled events with set event handler functions (on diagram/ component initilazation).
The problem is that inside these events the state is stale – it only gets the first state because of the closure.
const [otherState, setOtherState] = useState()
const handleMouseOverNode = (e) => {
// this only gets the initial value but doesn't update when state changes
console.log (otherState)
}
useEffect(() => {
cyRef.current.on('mouseover', 'node', handleMouseOverNode);
}
return () => {
cyRef.current.removeListener('mouseover');
};
}, [cyRef.current]);
How do I get the real (non stale) state ?
2
Answers
Your effect has a dependency on the
handleMouseOverNode
as it uses that function within it, so you should providehandleMouseOverNode
as a dependency in the dependency array:With this approach, your
useEffect
will recreate the event listener on every rerender, giving it access to call the newesthandleMouseOverNode
function that has access to the latest state. I suggest that you optimize this though, as a new function reference will be created on every rerender with the above approach. Instead, you can memoize the callback function so that it’s only recreated (and thus your even listeners are only re-added) when your function state changes by usinguseCallback()
:And then pass
handleMouseOverNode
as a dependency to theuseEffect()
as done above.The experimental
useEffectEvent
would solve this problem eventually. You can currently use it, but it’s not stable, and might break in the future (see Declaring an Effect Event):Currently the clunky solution is to use another ref to hold the updated value: