Last active
June 26, 2022 17:04
-
-
Save gragland/ae701852ae6159c712d860a946cd7ca0 to your computer and use it in GitHub Desktop.
React Hook recipe from https://usehooks.com. Demo: https://codesandbox.io/s/z64on3ypm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useRef, useEffect, useCallback } from 'react'; | |
// Usage | |
function App(){ | |
// State for storing mouse coordinates | |
const [coords, setCoords] = useState({ x: 0, y: 0 }); | |
// Event handler utilizing useCallback ... | |
// ... so that reference never changes. | |
const handler = useCallback( | |
({ clientX, clientY }) => { | |
// Update coordinates | |
setCoords({ x: clientX, y: clientY }); | |
}, | |
[setCoords] | |
); | |
// Add event listener using our hook | |
useEventListener('mousemove', handler); | |
return ( | |
<h1> | |
The mouse position is ({coords.x}, {coords.y}) | |
</h1> | |
); | |
} | |
// Hook | |
function useEventListener(eventName, handler, element = window){ | |
// Create a ref that stores handler | |
const savedHandler = useRef(); | |
// Update ref.current value if handler changes. | |
// This allows our effect below to always get latest handler ... | |
// ... without us needing to pass it in effect deps array ... | |
// ... and potentially cause effect to re-run every render. | |
useEffect(() => { | |
savedHandler.current = handler; | |
}, [handler]); | |
useEffect( | |
() => { | |
// Make sure element supports addEventListener | |
// On | |
const isSupported = element && element.addEventListener; | |
if (!isSupported) return; | |
// Create event listener that calls handler function stored in ref | |
const eventListener = event => savedHandler.current(event); | |
// Add event listener | |
element.addEventListener(eventName, eventListener); | |
// Remove event listener on cleanup | |
return () => { | |
element.removeEventListener(eventName, eventListener); | |
}; | |
}, | |
[eventName, element] // Re-run if eventName or element changes | |
); | |
}; |
You may add the following line element = !element ? window : element
before isSupported
constant in order to avoid the following error window is not defined
in Next.Js
projects. Also remove default element value in hook parameters!
I am wondering if the props like eventName
,event
and listener
will not change then why run effects again. If useEventListener
is run again why should we care about the new values, since the listener is already attached in the first place. What case am i missing?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@yossisp I don't know what you observed in the debugger but you must be misunderstanding, because the semantics of
useEffect
guarantees that the cleanup function will run (removing the old listener) before the function runs again (adding the new listener). And the cleanup function's closure will haveelement
bound to the old element.