In the below sandbox project, I was trying to use a custom hook to attach event listener.
In the useEventListener.js file, If I pass the savedHandler.current function directly to the event listener (line no: 21) I do not get the updated state value during state update. Only when I use the savedHandler.current function inside an anonymous function(line no: 19) I am getting the updated state value.
Could someone help me understand how this works ? Is this because of this reference?
https://codesandbox.io/s/gracious-cloud-kjw7dk?file=/src/useEventListener.js
Thanks
CodePudding user response:
This is the best I can explain:
For the context, when you click the toggle flag button, only your first useEffect(callback,[handler])
runs after EVERY rendering and your second useEffect(callback,[eventName,element])
runs ONLY ONCE after the initial rendering.
- Now when you pass savedHandler.current directly to the event listener, line 21, the event listener attaches function returned by
useCallback
as it is and hence on every event, the same function is being called due to reasons: The event is registered only ONCE and the cleanup function does not run since theuseEffect
runs only once after the initial rendering(the cleanup function will run in instances, just before the next side effect due to dependency changes, and when the component unmount). So you are registering the event only once with a memoized callback which persist across renderings. The update of savedHandler.current in the firstuseEffect
doesn't update the callback passed to event listener in the seconduseEffect
since it doesn't re-run and hence doesn't update the callback passed to the event listener. - Now when you use savedHandler.current function inside an anonymous function, the scenario is completely different here. When you pass such functions as a callback, on every event a new function is being invoked unlike the first instance. The anonymous callback of first event and second event is not same despite having the same code. So here you are not stuck with the same event listener callback that you passed previously and hence you have now access to the latest memoized savedHandler.current value inside the callback function updated by the first useEffect despite the second effect not running again.
This is it. To confirm for yourself, try to add handler as the dependency on your second useEffect
as well, and pass savedHandler.current directly to the event listener. You will get the updated state value as the useEffect
now runs after every update of handler and the event listener gets the latest callback to invoke upon.
And instead of creating another variable, you can directly do element.addEventListener(eventName, (event) => savedHandler.current(event));