Here is a popular useEvent
shim:
function useEvent(callback) {
const ref = useRef(() => {
throw new Error("Cannot call an event handler while rendering.")
})
// Or useInsertionEffect if it's React 18
useLayoutEffect(() => {
ref.current = callback
})
return useCallback((...args) => ref.current(...args), []);
}
// Usage
function Component(props) {
const [visible, setVisible] = useState(false)
// props.onToggle may not be stable
const onToggle = useEvent(props.onToggle)
// But our onToggle is stable
useEffect(() => onToggle(visible), [onToggle, visible])
// ❌ Throws when used in the render phase
onToggle(visible)
}
Source: https://tkdodo.eu/blog/refs-events-and-escape-hatches (gorgeous article by the way).
What I don't understand is why we need useLayoutEffect
here, and why we can't set the ref
value directly in the render function?
function useEvent(callback?) {
const ref = useRef(callback)
ref.current = callback
return useCallback((...args) => ref.current(...args), [])
}