I'm using react 16.8.0-alpha.1 with hooks and typescript and noticed this strange behavior. If I attach an event listener to the document or other element using the addEventListener method, the event handler method doesn't have access to the latest state variables.
I have created a codepen to reproduce the issue. The problem is that inside the handleScroll method the scrolled variable is always false, but in the useEffect method the variable is true.
Steps to reproduce:
1) Go to codepen.
2) Open the console.
3) Scroll the page.
4) Check the console output to see the different output from handleScroll and useEffect.
If you use a local variable or function in useEffect, you must specify it in dependencies array — or leave the array out completely.
React.useEffect(() => {
document.addEventListener("scroll", handleScroll); // <---- you're closing over handleScroll
return () => document.removeEventListener("scroll", handleScroll);
}, []); // <--------- empty array
The simple fix is to just remove the array. Remember that useEffect doesn't block paint and addEventListener is very fast. It's no big deal to re-execute it once in a while. Another way to fix your particular example is to move handleScroll inside your effect, and specify scrolled as a dependency in the array.
For the rare cases where some value changes very often but re-running the effect is very undesirable (e.g. because setting up a subscription itself has side effects or is expensive), you can use a mutable ref as escape hatch.
@gaearon Could you kindly explain the difference between putting handleScroll inside and outside the effect?
Adding the state into the dependency array worked for me
Most helpful comment
If you use a local variable or function in
useEffect, you must specify it in dependencies array — or leave the array out completely.The simple fix is to just remove the array. Remember that
useEffectdoesn't block paint andaddEventListeneris very fast. It's no big deal to re-execute it once in a while. Another way to fix your particular example is to movehandleScrollinside your effect, and specifyscrolledas a dependency in the array.For the rare cases where some value changes very often but re-running the effect is very undesirable (e.g. because setting up a subscription itself has side effects or is expensive), you can use a mutable ref as escape hatch.