Skip to content

Instantly share code, notes, and snippets.

@diegohaz
Last active April 27, 2023 15:06
Show Gist options
  • Star 39 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save diegohaz/695097a06f038a707c3a1b11e4e40195 to your computer and use it in GitHub Desktop.
Save diegohaz/695097a06f038a707c3a1b11e4e40195 to your computer and use it in GitHub Desktop.
type AnyFunction = (...args: any[]) => any
function useEvent<T extends AnyFunction>(callback?: T) {
const ref = useRef<AnyFunction | undefined>(() => {
throw new Error("Cannot call an event handler while rendering.")
})
// Or useInsertionEffect if it's React 18
useLayoutEffect(() => {
ref.current = callback
})
return useCallback<AnyFunction>((...args) => ref.current?.(...args), []) as T
}
// 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)
}
@artalar
Copy link

artalar commented Sep 28, 2022

type AnyFunction = (...args: any[]) => any;

function useEvent<T extends AnyFunction>(callback: T): T {
  const ref = React.useRef({
    stableFn: ((...args) => ref.current.callback(...args)) as T,
    callback,
  });
  // Or useInsertionEffect if it's React 18
  React.useLayoutEffect(() => {
    ref.current.callback = callback;
  });
  return ref.current.stableFn;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment