Can be interrupted/restarted by React
function Component({ prop }) {
console.log('1. Function body starts');
// STATE HOOKS - Initialize or return current state
const [state, setState] = useState(initial); // Returns current state
const [state, dispatch] = useReducer(fn, init); // Returns current state
// CONTEXT - Read context value (triggers re-render if changes)
const value = useContext(MyContext);
// REF - Returns stable object reference
const ref = useRef(initial);
// MEMOIZATION - Execute during render if deps changed
const memoValue = useMemo(() => compute(), [deps]);
const memoFn = useCallback(() => fn(), [deps]);
// TRANSITION (React 18+) - Non-blocking state updates
const [isPending, startTransition] = useTransition();
// DEFERRED VALUE (React 18+) - Delay re-render of expensive child
const deferredValue = useDeferredValue(value);
// ID - Stable unique ID for SSR hydration
const id = useId();
// EXTERNAL STORE (React 18+) - Subscribe to external data
const snapshot = useSyncExternalStore(subscribe, getSnapshot);
// OPTIMISTIC STATE (React 19+) - Optimistic UI updates
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
// ACTION STATE (React 19+, was useFormState) - Server actions
const [state, formAction] = useActionState(action, initialState);
// USE (React 19+) - Suspend on promise or read context
const data = use(promise); // Suspends component
const value = use(context); // Alternative to useContext
// DEBUG VALUE - No runtime effect (DevTools only)
useDebugValue(value, format);
console.log('2. Render complete, returning JSX');
return <div>...</div>;
}Synchronous, before React touches the DOM
useInsertionEffect(() => {
console.log('3. BEFORE DOM mutations (inject styles)');
// Used by CSS-in-JS libraries to inject styles
return () => console.log('Cleanup: useInsertionEffect');
}, [deps]);React commits changes to real DOM
4. React updates real DOM
Synchronous, blocks browser paint
useLayoutEffect(() => {
console.log('5. After DOM commit, BEFORE paint');
// Measure DOM, prevent flicker
const height = ref.current.offsetHeight;
return () => console.log('Cleanup: useLayoutEffect');
}, [deps]);
useImperativeHandle(ref, () => ({
console.log('5b. Expose imperative ref handle');
focus: () => inputRef.current.focus()
}), [deps]);6. Browser paints screen (visual update)
Asynchronous, non-blocking
useEffect(() => {
console.log('7. After paint (data fetching, subscriptions)');
// Async operations
const timer = setTimeout(() => {}, 1000);
return () => {
console.log('Cleanup: useEffect');
clearTimeout(timer);
};
}, [deps]);┌─ RENDER PHASE (interruptible) ─────────────────────┐
│ 1. Function body executes │
│ - useState/useReducer (initialize) │
│ - useContext (read) │
│ - useRef (create/return) │
│ - useMemo (compute) │
│ - useCallback (create) │
│ - useTransition (return state) │
│ - useDeferredValue (return value) │
│ - useId (return ID) │
│ - useSyncExternalStore (subscribe) │
│ - useOptimistic (return state) [React 19] │
│ - useActionState (return state) [React 19] │
│ - use (suspend or read) [React 19] │
│ - useDebugValue (DevTools only) │
│ 2. Return JSX │
└────────────────────────────────────────────────────┘
┌─ COMMIT PHASE (synchronous) ───────────────────────┐
│ 3. useInsertionEffect callbacks [React 18+] │
│ 4. React commits to real DOM │
│ 5. useLayoutEffect callbacks │
│ 6. useImperativeHandle (expose refs) │
└────────────────────────────────────────────────────┘
7. ═══ BROWSER PAINTS ═══
┌─ POST-PAINT (async) ───────────────────────────────┐
│ 8. useEffect callbacks │
└────────────────────────────────────────────────────┘
┌─ RENDER PHASE ──────────────────────────────────────┐
│ 1. Function body executes │
│ - useState/useReducer (return current state) │
│ - useMemo (re-compute if deps changed) │
│ - useCallback (recreate if deps changed) │
│ - ... all render-phase hooks ... │
│ 2. Return JSX │
└─────────────────────────────────────────────────────┘
┌─ COMMIT PHASE ──────────────────────────────────────┐
│ 3. useInsertionEffect cleanup (if deps changed) │
│ 4. useInsertionEffect callback (if deps changed) │
│ 5. React commits DOM updates │
│ 6. useLayoutEffect cleanup (if deps changed) │
│ 7. useLayoutEffect callback (if deps changed) │
│ 8. useImperativeHandle (if deps changed) │
└─────────────────────────────────────────────────────┘
9. ═══ BROWSER PAINTS ═══
┌─ POST-PAINT ────────────────────────────────────────┐
│ 10. useEffect cleanup (if deps changed) │
│ 11. useEffect callback (if deps changed) │
└─────────────────────────────────────────────────────┘
1. useInsertionEffect cleanup (all)
2. useLayoutEffect cleanup (all)
3. useEffect cleanup (all)
4. Component removed from DOM
| Hook | Phase | Timing |
|---|---|---|
useState |
Render | During function execution |
useReducer |
Render | During function execution |
useContext |
Render | During function execution |
useRef |
Render | During function execution |
useMemo |
Render | During function execution |
useCallback |
Render | During function execution |
useTransition |
Render | During function execution |
useDeferredValue |
Render | During function execution |
useId |
Render | During function execution |
useSyncExternalStore |
Render | During function execution |
useOptimistic |
Render | During function execution |
useActionState |
Render | During function execution |
use |
Render | During function execution (can suspend) |
useDebugValue |
Render | DevTools only |
useInsertionEffect |
Commit | Before DOM mutation |
useLayoutEffect |
Commit | After DOM, before paint |
useImperativeHandle |
Commit | After DOM, before paint |
useEffect |
Post-paint | After paint |
use(promise)- Suspend on promises (replaces some Suspense patterns)use(context)- Alternative touseContext(can be conditional)useOptimistic(state, updateFn)- Optimistic UI updatesuseActionState(action, initial)- Server actions (renamed fromuseFormState)
function Component() {
// Can be conditional (unlike useContext)!
if (condition) {
const theme = use(ThemeContext);
}
// Suspend on promise
const data = use(fetchData()); // Suspends until resolved
// Optimistic updates
const [messages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMsg) => [...state, newMsg]
);
// Server actions
const [state, formAction] = useActionState(async (prev, formData) => {
return await submitForm(formData);
}, initialState);
return <form action={formAction}>...</form>;
}- Function body runs twice on mount
- Effects run once (mount → cleanup → mount)
use(promise)suspends render, shows fallback- When resolved, re-renders from that hook
- Render phase can be interrupted/restarted
- Commit phase is always synchronous and atomic
function Component() {
// 1. RENDER PHASE - during function execution
adapter.renderStart();
const memo = useMemo(() => compute(), [deps]);
useInsertionEffect(() => {
// 2. COMMIT - before DOM mutation (CSS injection)
}, [deps]);
useLayoutEffect(() => {
// 3. COMMIT - after DOM, before paint (measurements)
}, [deps]);
useEffect(() => {
// 4. POST-PAINT - after paint (async operations)
}, [deps]);
return <div>...</div>;
}[]- Run once on mount only[deps]- Run on mount + when deps change- no array - Run on mount + every render
- Cleanup runs before next effect (if deps changed) + on unmount