19
loading...
This website collects cookies to deliver better user experience
const useEventListener = (type, callback) => {
React.useEffect(() => {
window.addEventListener(type, callback)
return () => {
window.removeEventListener(type, callback)
}
}, [])
}
type
and callback
dependency, so you add them.const useEventListener = (type, callback) => {
React.useEffect(() => {
window.addEventListener(type, callback)
return () => {
window.removeEventListener(type, callback)
}
}, [type, callback])
}
const useEventListener = (type, callback) => {
React.useEffect(() => {
console.log("subscribe")
window.addEventListener(type, callback)
return () => {
console.log("unsubscribe")
window.removeEventListener(type, callback)
}
}, [type, callback])
}
function Simple() {
useEventListener("resize", () => {
console.log("hello")
})
return <div>hello</div>
}
useEffect
in useEventListener
a lot more than you expected it to.function ExternalExample() {
const [count, setCount] = React.useState(0)
useEventListener("resize", () => {
setCount((prev) => prev + 1)
})
return (
<div>
<p>Count: {count}</p>
</div>
)
}
const useEventListener = (type, callback) => {
const callbackRef = React.useRef(null)
React.useEffect(() => {
console.log("assigning callback to refCallback")
callbackRef.current = callback
}, [callback])
React.useEffect(() => {
console.log("subscribe")
window.addEventListener(type, refCallback.current)
return () => {
console.log("unsubscribe")
window.removeEventListener(type, refCallback.current)
}
}, [type])
}
callback
is still getting updated on every count update, but only the useEffect
that's assigning callback
is running. This is avoiding the event listener from subscribing and unsubscribing! We also don't have to add refCallback.current
in the dependency array since updating refs do not trigger rerenders, which will not trigger a useEffect
execution.useEffect
dependency array, then feel free to stop here.const onSuccessRef = React.useRef(null)
const onErrorRef = React.useRef(null)
const apiRef = React.useRef(null)
const validateRef = React.useRef(null)
React.useEffect(() => {
onSuccessRef.current = onSuccess
}, [onSuccess])
React.useEffect(() => {
onErrorRef.current = onError
}, [onError])
React.useEffect(() => {
apiRef.current = api
}, [api])
React.useEffect(() => {
validateRef.current = validate
}, [validate])
useCallbackRef
const onSuccessRef = useCallbackRef(onSuccess)
const onErrorRef = useCallbackRef(onError)
const apiRef = useCallbackRef(api)
const validateRef = useCallbackRef(validate)
useCallbackRef
is written as follows:const useCallbackRef = (callback) => {
const callbackRef = React.useRef(null)
React.useEffect(() => {
callbackRef.current = callback
}, [callback])
return callbackRef
}
callbackRef
, it doesn't know that it's a ref!yarn add -D patch-package postinstall-postinstall
node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js
if (name === 'useRef' && id.type === 'Identifier') {
if ((name === 'useRef' || 'useCallbackRef') && id.type === 'Identifier') {
node_modules/.bin/patch-package eslint-plugin-react-hooks
"postinstall": "patch-package"
ESLint: The ref value 'callbackRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'callbackRef.current' to a variable inside the effect, and use that variable in the cleanup function.(react-hooks/exhaustive-deps)
callbackRef.current
to another variable such as callback
. You only have to do this when you're setting up subscriptions and unsubscribing from them in useEffects.callback
passed into useCallbackRef
as "dirty", and it complains if you try invoke it.