Skip to content

shadcn.io is an independent project built on shadcn/ui

Docs

Free

useEventCallback for React

Gives you one stable function that still reads the latest state and props. For React, when a child or a timer needs a fixed identity but fresh values.

useEventCallback returns a function that keeps the same identity and still calls your latest function.

Do not call it while rendering. Before the effect stores your function, that call throws. Afterward, a click or a timer sees the newest state. When the subscription itself should stay put, see useEventListener. In React pass this down when a child should not rerender only because the parent rendered.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

useEventCallback anatomy: useEventCallback(), save, fn

Usage

import { useEventCallback } from "@/hooks/use-event-callback";
const onClick = useEventCallback(() => {
  setCount(count + 1);
});

FAQ

When should I use useEventCallback instead of useCallback?

useCallback returns a new function when a dependency changes. useEventCallback returns one function for the life of the component, built with an empty dependency list. Your latest function is stored on a ref after commit and called from that stable wrapper. Use it when a child, an effect, or a timer should keep the same function and still see new state.

How does this avoid a stale closure?

A stale closure reads state from the render that created the function. This hook writes your function onto a ref after each commit, and the stable wrapper calls whatever is on that ref. The function from the latest commit closes over that render. The wrapper does not close over your state. A call during render, before the ref updates, still runs the previous function.

Can I call the returned function during render?

Not on the first render. That call throws Cannot call an event handler while rendering. The ref starts as that throwing function, and an effect replaces it with yours after commit. On a later render the call does not throw. It runs the function from the previous commit, because the ref updates after render. Call it from a click, an effect, or a timer.

What if I pass undefined?

The hook still returns a function. The overload that accepts undefined types the result as a function or undefined, but the implementation always returns the wrapper from useCallback. If the stored function is undefined, calling the wrapper returns undefined instead of throwing. Pass undefined when the handler is sometimes absent. A defined function keeps its own return type.

Which effect stores the function on the server?

On the server the hook uses useEffect, which does not run during that render, so the ref still holds the throwing function afterward. On the client it uses useLayoutEffect, which stores your function after DOM updates and before the browser paints. A click happens after that, so it sees your function. Do not call the wrapper during render on either side.

Do I have to pass type arguments?

No. The argument list and the return type are inferred from the function you pass. One overload requires a function and returns a function of the same shape. The other also accepts undefined. You can write the type arguments when inference is not enough, but the usual call does not need them. The wrapper is cast so a defined function does not look like it returns undefined.

API Reference

Parameters

ParameterType
fn((...args: Args) => R) | undefined

The function to call; undefined makes the stable function return undefined.

Type
((...args: Args) => R) | undefined

Returns

ValueType
returns((...args: Args) => R) | undefined

A stable function with fn's arguments and result.

Type
((...args: Args) => R) | undefined