SuperAPI AI Directory

react-syntax-hooks-advanced

Frontend & websites

View repository

Updated 10-16 11:57

About this skill

react-syntax-hooks-advanced

Quick Reference

Advanced Hooks at a Glance

Hook Version Purpose Returns
useId 18+ SSR-safe unique IDs for accessibility string
useTransition 18+ Mark state updates as non-blocking [isPending, startTransition]
useDeferredValue 18+ Defer expensive re-renders T (deferred value)
useSyncExternalStore 18+ Subscribe to external stores T (store snapshot)
useInsertionEffect 18+ Inject styles before DOM mutations void
useDebugValue 18+ Label custom hooks in DevTools void
use 19+ Read promises (Suspense) and context conditionally T
useActionState 19+ Manage async action state with pending flag [state, dispatch, isPending]
useOptimistic 19+ Optimistic UI during async actions [optimisticState, setOptimistic]
useFormStatus 19+ Read parent form submission state {pending, data, method, action}

Critical Warnings

NEVER use useId to generate keys for list items -- keys MUST come from your data. useId generates IDs for accessibility attributes only.

NEVER use useInsertionEffect in application code -- it exists ONLY for CSS-in-JS library authors. Use useEffect or useLayoutEffect instead.

NEVER call useFormStatus in the same component that renders the <form> -- it MUST be called from a child component rendered inside the form.

NEVER call use() inside a try/catch block -- use an Error Boundary for error handling with promises.

NEVER wrap controlled text input updates in startTransition -- transitions are interruptible and will cause the input to feel broken.


Decision Tree: Which Advanced Hook?

Need a unique ID for aria/htmlFor?
  YES --> useId

Need to keep UI responsive during expensive state update?
  YES --> Do you have the state setter?
           YES --> useTransition (wrap setState in startTransition)
           NO  --> useDeferredValue (defer the prop/value)

Need to subscribe to non-React state (Redux, browser API, etc.)?
  YES --> useSyncExternalStore

Need to inject <style> tags before DOM mutations?
  YES --> Are you building a CSS-in-JS library?
           YES --> useInsertionEffect
           NO  --> useEffect or useLayoutEffect

Need to read a Promise with Suspense? (React 19)
  YES --> use(promise)

Need to read context conditionally? (React 19)
  YES --> use(context)

Need form action with pending state and error handling? (React 19)
  YES --> useActionState

Need instant UI feedback before async action completes? (React 19)
  YES --> useOptimistic

Need form submission status in a child component? (React 19)
  YES --> useFormStatus

React 18 Advanced Hooks

useId

Generates a unique string ID that is stable across server and client renders.

import { useId } from 'react';

function EmailField(): JSX.Element {
  const id = useId();
  return (
    <div>
      <label htmlFor={id + '-email'}>Email</label>
      <input id={id + '-email'} type="email" aria-describedby={id + '-hint'} />
      <p id={id + '-hint'}>We will never share your email.</p>
    </div>
  );
}

ALWAYS use useId as a prefix when generating multiple related IDs. ALWAYS pass identifierPrefix to the root when running multiple React apps on the same page.


useTransition

Marks state updates as non-urgent so they do not block user interaction.

import { useState, useTransition } from 'react';

function TabContainer(): JSX.Element {
  const [tab, setTab] = useState<string>('home');
  const [isPending, startTransition] = useTransition();

  function selectTab(nextTab: string): void {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <div>
      <button onClick={() => selectTab('about')}>About</button>
      {isPending && <span>Loading...</span>}
      <TabPanel tab={tab} />
    </div>
  );
}

startTransition executes its callback immediately -- the state update inside is what becomes interruptible. ALWAYS wrap startTransition inside setTimeout, NEVER the other way around. State updates after await inside an async startTransition require a nested startTransition call.


useDeferredValue

Returns a deferred copy of a value that lags behind during urgent updates.

import { useDeferredValue, useMemo } from 'react';

function SearchResults({ query }: { query: string }): JSX.Element {
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

  const results = useMemo(() => filterResults(deferredQuery), [deferredQuery]);

  return (
    <div style={{ opacity: isStale ? 0.5 : 1 }}>
      <ResultsList items={results} />
    </div>
  );
}

React 19+ adds an optional initialValue parameter:

const deferredQuery = useDeferredValue(query, ''); // '' on first render

ALWAYS use primitives or objects created outside the render function. Passing a new object every render defeats the purpose of deferral.


useSyncExternalStore

Subscribes a component to an external (non-React) data source with tear-free reads.

import { useSyncExternalStore } from 'react';

function useOnlineStatus(): boolean {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine,          // client snapshot
    () => true                        // server snapshot (SSR)
  );
}

function subscribe(callback: () => void): () => void {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

ALWAYS declare the subscribe function outside the component or wrap it in useCallback -- a new function reference causes resubscription every render. getSnapshot MUST return immutable data; returning a new object every call causes an infinite re-render loop.


useInsertionEffect

Fires before React makes any DOM changes. Reserved for CSS-in-JS libraries.

import { useInsertionEffect } from 'react';

// ONLY for CSS-in-JS library authors
function useCSS(rule: string): string {
  useInsertionEffect(() => {
    const style = document.createElement('style');
    style.textContent = rule;
    document.head.appendChild(style);
    return () => { document.head.removeChild(style); };
  });
  return rule;
}

Cannot call setState inside. Refs are NOT attached yet. Does NOT run during SSR.


useDebugValue

Adds a label to custom hooks visible in React DevTools.

import { useDebugValue } from 'react';

function useOnlineStatus(): boolean {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
  useDebugValue(isOnline ? 'Online' : 'Offline');
  return isOnline;
}

ALWAYS pass a formatter function for expensive formatting to avoid cost when DevTools is closed:

useDebugValue(date, (d: Date) => d.toISOString());

React 19 Hooks

use()

React 19+

Reads a Promise (triggering Suspense) or a Context value. Unlike all other hooks, use() CAN be called inside conditionals, loops, and after early returns.

import { use, Suspense } from 'react';

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }): JSX.Element {
  const comments = use(commentsPromise);
  return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>;
}

// Conditional context reading -- impossible with useContext
function Heading({ children }: { children: React.ReactNode | null }): JSX.Element | null {
  if (children == null) return null;
  const theme = use(ThemeContext);
  return <h1 style={{ color: theme.color }}>{children}</h1>;
}

ALWAYS create promises in Server Components and pass them to Client Components -- promises created during client render are recreated every render. ALWAYS wrap the consuming component in <Suspense> and <ErrorBoundary>.


useActionState

React 19+

Manages state from form actions with built-in pending tracking. Replaces the deprecated useFormState.

import { useActionState } from 'react';

async function submitForm(
  prevState: { error: string | null },
  formData: FormData
): Promise<{ error: string | null }> {
  const name = formData.get('name') as string;
  if (!name) return { error: 'Name is required' };
  await saveToDatabase(name);
  return { error: null };
}

function ContactForm(): JSX.Element {
  const [state, dispatch, isPending] = useActionState(submitForm, { error: null });

  return (
    <form action={dispatch}>
      <input name="name" disabled={isPending} />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Saving...' : 'Save'}
      </button>
      {state.error && <p className="error">{state.error}</p>}
    </form>
  );
}

Multiple dispatches are queued sequentially -- each receives the previous action's return value. Errors cancel all queued actions and trigger the nearest Error Boundary.


useOptimistic

React 19+

Shows an optimistic state while an async action is in progress. Automatically reverts when the action completes.

import { useOptimistic, startTransition } from 'react';

function LikeButton({ liked, onToggle }: {
  liked: boolean;
  onToggle: (next: boolean) => Promise<void>;
}): JSX.Element {
  const [optimisticLiked, setOptimisticLiked] = useOptimistic(liked);

  function handleClick(): void {
    startTransition(async () => {
      setOptimisticLiked(!optimisticLiked);
      await onToggle(!optimisticLiked);
    });
  }

  return <button onClick={handleClick}>{optimisticLiked ? 'Unlike' : 'Like'}</button>;
}

setOptimistic MUST be called inside a startTransition or an Action prop. The reducer (if provided) MUST be pure -- no side effects.


useFormStatus

React 19+

Reads the submission status of the nearest parent <form>. Imported from react-dom.

import { useFormStatus } from 'react-dom';

function SubmitButton(): JSX.Element {
  const { pending, data } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

function MyForm(): JSX.Element {
  return (
    <form action={handleSubmit}>
      <input name="email" type="email" />
      <SubmitButton />  {/* MUST be a child of <form> */}
    </form>
  );
}

Returns { pending, data, method, action }. pending is ALWAYS false when called outside a <form> or in the same component that renders the <form>.


Reference Links

Official Sources