19
The problem with callbacks depending on state inside useEffect you might not be aware of!
While writing React code. I have come across an issue that was really hard to figure out why it happened, but had a really fix, and so I thought I'd publish it for future devs to be aware of!
So you might have a react functional component that uses the useState
hook, some useEffect
s and that has some complicated logic inside a listener, and so you've placed the logic inside a callback that references the state:
const MyComponent = () => {
const [myState, setMyState] = useState()
const myHelper = () => {
/* does stuff based on myState in here*/
}
useEffect(() => {
addListener(myHelper())
}, [/* some dependencies */])
return (<div/>)
}
What you might notice is, when the state changes, the handler being executed by the listener still uses the old state!
It seems like the it captures its environment and uses that!
This can be really confusing to figure out why it does this.
To solve this, you need to do two things:
- add the state to the dependency array
- remove the listener on state change
useEffect(() => {
// store the listener to be able to remove it later
const listener = addListener(myHelper);
return () => {
// actually remove the listener in the cleanup
listener.remove();
};
// add myState to the dependencies
}, [myState, /* other dependencies*/])
And that's it!
This might seem obvious at first, add all the states that the useEffect
depends on inside the dependencies array.
But the tricky part here is, you might have more dependencies than you think at first!
It's not just the states directly used inside the useEffect
hook, but it's also all the states used by functions that are called in here!
So the next time you think of what dependencies need to go in the dependency array, don't just look at what states are used inside the hook, but also at which ones are used in functions inside the hook!
19