Topics in this subject
React 3 min read Updated 5 Aug 2026

Other Hooks

Less-common hooks: useLayoutEffect, useId, useSyncExternalStore, useImperativeHandle, and useDebugValue.

The core idea#

Beyond useState/useEffect/useMemo/useCallback, React ships specialized hooks for DOM measurement, stable IDs, external stores, imperative handles, and devtools labels. Knowing when each applies is a common senior signal.

useLayoutEffect vs useEffect#

useLayoutEffect fires synchronously after DOM mutations but before the browser paints — use it to measure layout and mutate the DOM without a visible flicker. useEffect fires after paint, asynchronously.

function Tooltip({ targetRef }) {
  const [pos, setPos] = useState({ top: 0 });
  useLayoutEffect(() => {
    const rect = targetRef.current.getBoundingClientRect(); // measure
    setPos({ top: rect.bottom });                            // reposition before paint
  }, []);
  return <div style={{ top: pos.top }} />;
}

⚠️ useLayoutEffect blocks paint — overuse hurts performance, and it warns during SSR (no DOM). Default to useEffect; reach for useLayoutEffect only to prevent a visual flash.

useId#

Generates a stable, SSR-safe unique id consistent across server and client — for linking label/input, aria-describedby, etc. Not for list keys.

const id = useId();
<label htmlFor={id}>Email</label>
<input id={id} />

useSyncExternalStore#

Subscribes to an external store (Redux, Zustand, a browser API) in a concurrent-safe way, avoiding tearing. Store libraries use it internally; you use it directly to wrap browser state.

function useOnlineStatus() {
  return useSyncExternalStore(
    (cb) => { window.addEventListener("online", cb);
              window.addEventListener("offline", cb);
              return () => { /* cleanup */ }; },
    () => navigator.onLine,      // client snapshot
    () => true                   // server snapshot
  );
}

useImperativeHandle (+ forwardRef)#

Customizes the ref value a parent receives, exposing a narrow imperative API instead of the raw DOM node.

const Input = forwardRef((props, ref) => {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus(),
    clear: () => { inputRef.current.value = ""; },
  }));
  return <input ref={inputRef} {...props} />;
});
// parent: inputRef.current.focus()

🟢 React 19 lets you pass ref as a regular prop, so many forwardRef wrappers become unnecessary — but useImperativeHandle is still how you expose a custom handle.

useDebugValue#

Labels a custom hook in React DevTools — no runtime effect, dev-only.

useDebugValue(isOnline ? "Online" : "Offline");

When to use each#

Hook Use when
useLayoutEffect Measure/mutate DOM before paint to avoid flicker
useEffect Everything else — data, subscriptions, side effects (default)
useId Generate SSR-stable ids for accessibility attributes
useSyncExternalStore Subscribe to a store/browser API without tearing
useImperativeHandle Expose a limited imperative API via ref
useDebugValue Label a reusable custom hook in DevTools
flowchart TD
  A["Effect scheduled"] --> B{"Need DOM measurement<br/>before paint?"}
  B -->|"Yes"| C["useLayoutEffect — sync, blocks paint"]
  B -->|"No"| D["useEffect — async, after paint"]

Interview Q&A#

Q1. useLayoutEffect vs useEffect — timing and use case? useLayoutEffect runs synchronously after DOM mutation but before paint, so you can measure and reposition without flicker (tooltips, autosizing). useEffect runs after paint, asynchronously — the default for data fetching and subscriptions since it doesn't block rendering.

Q2. Why does useId exist instead of Math.random()? Random ids differ between server and client, causing hydration mismatches. useId produces a deterministic id stable across SSR and the client, safe for htmlFor/aria-*. It is not meant for list keys.

Q3. What problem does useSyncExternalStore solve? Concurrent rendering can "tear" — different components reading an external store mid-render see inconsistent values. useSyncExternalStore gives React a subscribe function and a snapshot getter so all reads stay consistent, plus an SSR snapshot. Store libraries build on it.

Q4. When would you reach for useImperativeHandle? When a parent legitimately needs to call methods on a child (focus, scroll, play/pause a media element). Combined with forwardRef, it exposes a curated API instead of the raw DOM node. Prefer props/state first; use it only for genuinely imperative actions.

Q5. Does useDebugValue affect production? No. It only sets a label shown in React DevTools for custom hooks. React may skip evaluating its (optional) formatting function unless DevTools is open.