@knitui/hooks
useCallbackRef
Wrap a callback so its identity stays stable across renders while always invoking the latest version — port of Mantine's useCallbackRef. Lets event handlers and timers avoid stale closures without re-subscribing. This is the "always-latest" pattern hand-rolled as stateRef inside use-move / use-radial-move; pure React, so identical on web and native.
Import
import { useCallbackRef } from "@knitui/hooks";Signature
export function useCallbackRef<Args extends unknown[], Return>( callback: ((...args: Args) => Return) | undefined, ): (...args: Args) => Return | undefineduseCallbackRef solves the tension between the two things you normally have to
trade off. useCallback with a dependency list gives you a stable identity but
goes stale the moment you forget a dependency; a plain inline function is always
current but changes identity every render, so anything that subscribes to it
(an effect, an event listener, a timer, a memoised child) re-subscribes every
render. useCallbackRef gives you both: the returned function's identity never
changes, and it always forwards to the most recently rendered callback.
It works by writing callback into a ref on every render and returning a
useCallback with an empty dependency list. That means dependency arrays on
effects that use the returned function can genuinely be empty — the closure is
not captured.
This is the pattern the kit uses to keep event handlers stale-free without
re-wiring listeners: TreeSelect, Autocomplete, PinInput, MaskInput and
the carousel Pagination all build their handlers this way.
When to use it
- A handler passed to an effect that subscribes once and must not re-subscribe.
- Callbacks handed to timers, gesture handlers or imperative subscriptions.
- Props on a memoised child that would otherwise re-render on every parent render.
Notes
- The wrapped
callbackmay beundefined; the returned function then returnsundefinedrather than throwing, which is why the return type isReturn | undefined. - The ref is assigned in a
useEffect, not during render. A call made during render or from auseLayoutEffectin the same commit therefore still runs the previous callback. - Pure React, no platform code — identical on web and native.