React 18: Concurrent Rendering and Performance Optimization
Concurrent Rendering and Time-Slicing
React 18 introduces concurrent rendering: React yields to browser after 5ms (time-slice) to handle user input (click, typing). Previously, long-running renders block main thread. Reconciliation (virtual DOM diffing) happens in background, paint happens when complete. Example: rendering 10K items normally takes 200ms (browser freezes). With concurrent rendering (priority-based): user input response <50ms, rendering completes 200ms but user sees progress. useTransition hook marks non-urgent updates (search results, filters) deferrable. USeDeferredValue hook defers derived state computation.
Automatic Batching and Performance
React 18 batches multiple state updates into single render: setState → event handler completes → React renders once (previously rendered multiple times if async). Batching prevents unnecessary renders: 50% render reduction typical. Example: form with name + email fields, changing both fields previously triggered 2 renders, now triggers 1. Manual opt-out via flushSync for rare cases requiring immediate DOM updates. Fiber architecture rewritten in concurrent era: React pauses render mid-way, prioritizes updates (urgent vs non-urgent).
Suspense for Data Fetching
Suspense manages async data fetching in component trees. Server-side data fetching: wrap async component in Suspense boundary, show fallback UI (loading spinner) while fetching. Error boundaries catch fetch failures. Example: ProductDetails component fetches from API in render, throws Promise (intentional), Suspense catches, shows fallback, when data ready component renders. Code-splitting: dynamic import() returns Promise, Suspense boundary shows fallback while bundle downloads. Production: Suspense streaming from server (renderToPipeableStream) sends initial HTML skeleton + CSS immediately, JavaScript loads incrementally, hydration progresses component-by-component (Selective Hydration).
New Hooks: useTransition and useDeferredValue
useTransition: isPending, startTransition(callback). Marks state update low-priority. Example: search field → setState in onChange (high-priority, immediate response) → startTransition searches database (low-priority, deferred). User types smoothly, search results update after. startTransition prevents UI blocking. useDeferredValue(value): Similar but for derived state. Example: expensive list filter: value(input) high-priority, deferredValue (filtered list) low-priority. Decouples urgent updates (input focus, cursor position) from expensive computations. useId() generates consistent IDs across server/client for form inputs, avoiding hydration mismatches. useInsertionEffect() fires before ALL effects, used by CSS-in-JS libraries injecting styles into DOM head.
Upgrade Path and Breaking Changes
React 16 → 17 → 18 migrations: Root API changes (createRoot vs ReactDOM.render). Automatic batching breaks code assuming synchronous rendering (flushSync workaround). Suspense for data only works with compatible libraries (Next.js, Remix). StrictMode renders components twice in development (detecting side effects). Legacy APIs deprecated (findDOMNode, string refs). Performance gains: 30-50% faster renders on complex UIs (1000+ components), 80% faster for list updates with keys. Memory usage: similar (React Fiber still ~5MB bundle).