React 18+ Concurrent Features and State

1. useTransition Hook for Non-blocking State Updates

Feature API Description Use Case
useTransition const [isPending, startTransition] = useTransition() Mark state updates as non-urgent transitions Keep UI responsive during heavy updates (search, filtering, tabs)
isPending boolean Indicates if transition is in progress Show loading indicators during transition
startTransition startTransition(() => setState(...)) Wrap low-priority state updates Allow urgent updates (typing) to interrupt slow updates (filtering)
Priority System Urgent vs Transition updates React prioritizes urgent updates over transitions Input responsiveness remains high during background work
Interruptible Transitions can be abandoned New urgent updates cancel ongoing transitions No stale results when user types quickly
No Suspense Needed Works without Suspense boundaries Simpler than Suspense for CPU-bound work Heavy computations, large list rendering
Batching All updates in startTransition batched Single re-render for multiple state updates Performance optimization for complex updates
Concurrent Rendering Enables React 18 concurrent features Render can be paused and resumed Smooth UI even with expensive renders

Example: Search with transitions for responsive input

import { useState, useTransition } from 'react';

function SearchList({ items }) {
  const [query, setQuery] = useState('');
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (value) => {
    // Urgent update - input stays responsive
    setQuery(value);
    
    // Non-urgent update - can be interrupted
    startTransition(() => {
      // Heavy filtering operation
      const filtered = items.filter(item =>
        item.title.toLowerCase().includes(value.toLowerCase()) ||
        item.description.toLowerCase().includes(value.toLowerCase())
      );
      setFilteredItems(filtered);
    });
  };
  
  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => handleSearch(e.target.value)}
        placeholder="Search..."
      />
      
      {isPending && <span>Searching...</span>}
      
      <div style={{ opacity: isPending ? 0.5 : 1 }}>
        {filteredItems.map(item => (
          <ItemCard key={item.id} item={item} />
        ))}
      </div>
    </div>
  );
}

// Without useTransition - input becomes sluggish
function SlowSearch({ items }) {
  const [query, setQuery] = useState('');
  
  // Both updates are urgent - blocks input
  const filteredItems = items.filter(item =>
    item.title.toLowerCase().includes(query.toLowerCase())
  );
  
  return <div>...</div>;
}

Example: Tab switching with smooth transitions

import { useState, useTransition } from 'react';

function TabContainer() {
  const [activeTab, setActiveTab] = useState('about');
  const [isPending, startTransition] = useTransition();
  
  const switchTab = (tab) => {
    startTransition(() => {
      setActiveTab(tab);
    });
  };
  
  return (
    <div>
      <div className="tabs">
        {['about', 'posts', 'contact'].map(tab => (
          <button
            key={tab}
            onClick={() => switchTab(tab)}
            className={activeTab === tab ? 'active' : ''}
            disabled={isPending}
          >
            {tab}
          </button>
        ))}
      </div>
      
      <div style={{ opacity: isPending ? 0.7 : 1 }}>
        {activeTab === 'about' && <AboutTab />}
        {activeTab === 'posts' && <PostsTab />} {/* Expensive */}
        {activeTab === 'contact' && <ContactTab />}
      </div>
    </div>
  );
}

// Complex tab that benefits from transition
function PostsTab() {
  // Expensive rendering - thousands of items
  const posts = useMemo(() => 
    generatePosts(10000), []
  );
  
  return (
    <div>
      {posts.map(post => (
        <PostCard key={post.id} post={post} />
      ))}
    </div>
  );
}
Note: useTransition keeps input responsive by deprioritizing expensive updates. Use for filtering, searching, tab switching, or any CPU-intensive state changes. The isPending flag lets you show loading states or reduce opacity during transitions.

2. useDeferredValue for State Value Deferring

Feature API Description Difference from useTransition
useDeferredValue const deferredValue = useDeferredValue(value) Defer rendering with stale value For values you don't control (props), useTransition for state you own
Debouncing Effect Shows previous value during update Similar to debouncing but integrated with React Automatic unlike manual setTimeout debouncing
Memoization Combine with useMemo/memo Prevent re-render of expensive components Deferred value triggers re-render only when ready
No isPending No loading state flag Compare value !== deferredValue for pending useTransition provides isPending flag
Background Update Deferred update happens in background UI stays responsive during update Similar priority to startTransition
Initial Render First render uses actual value Only subsequent updates deferred No delay on initial mount
Cancellation New value cancels pending deferred update Prevents stale results Interruptible like transitions
Use Cases Expensive computations, large lists When you receive value from parent useTransition when you control the state

Example: Deferred search results

import { useState, useDeferredValue, memo } from 'react';

function SearchApp() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  
  // Check if deferred value is stale
  const isStale = query !== deferredQuery;
  
  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      
      {/* Input updates immediately */}
      <div>Searching for: {query}</div>
      
      {/* Results update with delay */}
      <div style={{ opacity: isStale ? 0.5 : 1 }}>
        <SearchResults query={deferredQuery} />
      </div>
    </div>
  );
}

// Expensive component that benefits from deferred value
const SearchResults = memo(function SearchResults({ query }) {
  const items = useMemo(() => {
    // Expensive filtering/search operation
    return hugeList.filter(item =>
      item.toLowerCase().includes(query.toLowerCase())
    );
  }, [query]);
  
  return (
    <ul>
      {items.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
});

Example: Deferred value vs regular value comparison

import { useState, useDeferredValue, memo } from 'react';

function SliderDemo() {
  const [value, setValue] = useState(0);
  const deferredValue = useDeferredValue(value);
  
  return (
    <div>
      <input
        type="range"
        min="0"
        max="100"
        value={value}
        onChange={(e) => setValue(Number(e.target.value))}
      />
      
      <div>
        <div>Current: {value}</div>
        <div>Deferred: {deferredValue}</div>
      </div>
      
      {/* Slider updates immediately */}
      <FastComponent value={value} />
      
      {/* Chart updates with delay, stays responsive */}
      <SlowChart value={deferredValue} />
    </div>
  );
}

const SlowChart = memo(function SlowChart({ value }) {
  // Simulate expensive render
  const chartData = useMemo(() => {
    const data = [];
    for (let i = 0; i < 10000; i++) {
      data.push(Math.sin(i / 100) * value);
    }
    return data;
  }, [value]);
  
  return <Canvas data={chartData} />;
});

// Without useDeferredValue - slider becomes laggy
function LaggySlider() {
  const [value, setValue] = useState(0);
  
  return (
    <div>
      <input
        type="range"
        value={value}
        onChange={(e) => setValue(Number(e.target.value))}
      />
      {/* Both update together - slider lags */}
      <SlowChart value={value} />
    </div>
  );
}
Note: useDeferredValue is ideal when you receive a frequently changing value from props. Use with memo() to prevent re-renders until deferred value updates. For state you control directly, prefer useTransition with startTransition.

3. Concurrent Rendering and State Consistency

Concept Description Implications Best Practices
Concurrent Rendering React can pause, resume, or abandon renders Renders may happen multiple times before commit Render functions must be pure, no side effects
Tearing Different parts showing different state snapshots Can occur with external stores in concurrent mode Use useSyncExternalStore to prevent tearing
State Snapshots Each render gets consistent state snapshot State doesn't change mid-render Rely on this for consistent derived values
Automatic Batching All state updates batched (even in async) Fewer re-renders in React 18 No manual batching needed in most cases
flushSync flushSync(() => setState(...)) Force synchronous update (opt-out batching) Use sparingly, only when absolutely needed
Strict Mode Double-invokes functions in development Catches impure render functions Keep renders pure, no mutations or side effects
Time Slicing Long renders split into chunks Browser stays responsive during heavy work Enabled automatically with transitions
Work Prioritization High priority work interrupts low priority User interactions stay responsive Mark background updates as transitions

Example: Automatic batching in React 18

import { useState } from 'react';
import { flushSync } from 'react-dom';

function Counter() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  
  console.log('Render count:', count, 'flag:', flag);
  
  // React 18: Single re-render (batched)
  const handleClick = () => {
    setCount(c => c + 1);
    setFlag(f => !f);
    // Only ONE render, not two
  };
  
  // React 18: Even async updates are batched
  const handleAsync = async () => {
    await fetch('/api/data');
    setCount(c => c + 1);
    setFlag(f => !f);
    // Still only ONE render!
  };
  
  // React 18: Timeouts also batched
  const handleTimeout = () => {
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // Still batched!
    }, 1000);
  };
  
  // Opt-out of batching with flushSync (rarely needed)
  const handleFlushSync = () => {
    flushSync(() => {
      setCount(c => c + 1); // Render immediately
    });
    setFlag(f => !f); // Second render
    // TWO renders
  };
  
  return (
    <div>
      <p>Count: {count}, Flag: {flag ? 'true' : 'false'}</p>
      <button onClick={handleClick}>Sync Update</button>
      <button onClick={handleAsync}>Async Update</button>
      <button onClick={handleTimeout}>Timeout Update</button>
      <button onClick={handleFlushSync}>FlushSync (not batched)</button>
    </div>
  );
}

Example: Pure render functions for concurrent mode

import { useState } from 'react';

// ❌ IMPURE - Will cause issues in concurrent mode
let renderCount = 0;
function ImpureComponent() {
  renderCount++; // Side effect during render!
  const [state, setState] = useState(0);
  
  // Double counting in StrictMode/concurrent renders
  return <div>Renders: {renderCount}</div>;
}

// ✅ PURE - Safe for concurrent mode
function PureComponent() {
  const [state, setState] = useState(0);
  const [renderCount, setRenderCount] = useState(0);
  
  // Track renders in effect, not during render
  useEffect(() => {
    setRenderCount(c => c + 1);
  });
  
  return <div>Renders: {renderCount}</div>;
}

// ❌ IMPURE - Mutating external object
const cache = { data: null };
function MutatingComponent({ id }) {
  cache.data = fetchData(id); // Mutation during render!
  return <div>{cache.data}</div>;
}

// ✅ PURE - Use state or refs for caching
function NonMutatingComponent({ id }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetchData(id).then(setData);
  }, [id]);
  
  return <div>{data}</div>;
}

// ✅ PURE - Derived values calculated during render
function DerivedComponent({ items }) {
  // Pure computation - no side effects
  const total = items.reduce((sum, item) => sum + item.price, 0);
  const average = total / items.length;
  
  return <div>Average: {average}</div>;
}
Warning: Concurrent rendering can call render functions multiple times before committing. Keep renders pure - no mutations, no side effects, no external state changes. Use useEffect for side effects, not render phase.

4. useSyncExternalStore for External State Integration

Feature API Description Purpose
useSyncExternalStore useSyncExternalStore(subscribe, getSnapshot) Subscribe to external stores safely Prevent tearing in concurrent mode
subscribe (callback) => unsubscribe Register listener for store changes React re-renders when store updates
getSnapshot () => currentValue Return current store value Must return same value for same store state
getServerSnapshot () => serverValue Optional server-side value SSR hydration without mismatch
Tearing Prevention Ensures consistent state across tree All components see same snapshot Critical for concurrent rendering
External Stores Redux, Zustand, custom stores, browser APIs Any non-React state source Safe integration with React 18
Selector Function getSnapshot: () => store.getState().slice Select specific state slice Optimize re-renders, only subscribe to needed data
Memoization getSnapshot must be stable or memoized Prevent infinite loops Return same reference for same value

Example: Custom store with useSyncExternalStore

import { useSyncExternalStore } from 'react';

// External store (not React state)
const createStore = (initialState) => {
  let state = initialState;
  const listeners = new Set();
  
  return {
    getState: () => state,
    setState: (newState) => {
      state = newState;
      listeners.forEach(listener => listener());
    },
    subscribe: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    }
  };
};

const counterStore = createStore(0);

// Hook to use external store
function useCounter() {
  const count = useSyncExternalStore(
    counterStore.subscribe,
    counterStore.getState
  );
  
  return count;
}

// Component usage
function Counter() {
  const count = useCounter();
  
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => counterStore.setState(count + 1)}>
        Increment
      </button>
    </div>
  );
}

// Multiple components stay in sync
function AnotherCounter() {
  const count = useCounter();
  return <div>Count: {count}</div>;
}

Example: Browser API integration with selectors

import { useSyncExternalStore } from 'react';

// Online status hook
function useOnlineStatus() {
  const isOnline = useSyncExternalStore(
    (callback) => {
      window.addEventListener('online', callback);
      window.addEventListener('offline', callback);
      return () => {
        window.removeEventListener('online', callback);
        window.removeEventListener('offline', callback);
      };
    },
    () => navigator.onLine,
    () => true // Server snapshot
  );
  
  return isOnline;
}

// Window size hook
function useWindowSize() {
  const size = useSyncExternalStore(
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    () => ({ 
      width: window.innerWidth, 
      height: window.innerHeight 
    }),
    () => ({ width: 0, height: 0 }) // Server
  );
  
  return size;
}

// Media query hook
function useMediaQuery(query) {
  const matches = useSyncExternalStore(
    (callback) => {
      const mediaQuery = window.matchMedia(query);
      mediaQuery.addEventListener('change', callback);
      return () => mediaQuery.removeEventListener('change', callback);
    },
    () => window.matchMedia(query).matches,
    () => false // Server
  );
  
  return matches;
}

// Component usage
function ResponsiveComponent() {
  const isOnline = useOnlineStatus();
  const { width } = useWindowSize();
  const isMobile = useMediaQuery('(max-width: 768px)');
  
  return (
    <div>
      <div>Status: {isOnline ? 'Online' : 'Offline'}</div>
      <div>Width: {width}px</div>
      <div>Mobile: {isMobile ? 'Yes' : 'No'}</div>
    </div>
  );
}

Example: Redux integration with selectors

import { useSyncExternalStore } from 'react';

// Custom hook for Redux with selector
function useSelector(selector) {
  return useSyncExternalStore(
    store.subscribe,
    () => selector(store.getState()),
    () => selector(store.getState()) // Server
  );
}

// Component usage
function UserProfile() {
  const user = useSelector(state => state.user);
  const theme = useSelector(state => state.ui.theme);
  
  return (
    <div className={theme}>
      <h1>{user.name}</h1>
    </div>
  );
}

// Memoized selector to prevent infinite loops
function TodoList() {
  const selectTodos = useMemo(
    () => (state) => state.todos.filter(t => !t.completed),
    []
  );
  
  const activeTodos = useSelector(selectTodos);
  
  return (
    <ul>
      {activeTodos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}
Note: useSyncExternalStore is essential for library authors and when integrating external stores (Redux, browser APIs). It prevents tearing in concurrent mode by ensuring all components read the same snapshot. Most app developers won't use it directly.

5. State Prioritization and Update Scheduling

Priority Level Type Examples Behavior
Urgent/Discrete User interactions, controlled inputs Clicks, typing, hover, focus Executed immediately, highest priority
Transition Non-urgent UI updates Search results, filtering, navigation Can be interrupted, lower priority
Deferred Background updates Analytics, logging, prefetch Lowest priority, runs when idle
Lane System Internal React priority mechanism 32 priority lanes for scheduling Fine-grained control of update order
Interruption Higher priority cancels lower Typing cancels pending search update Ensures UI responsiveness
Batching Same priority updates grouped Multiple setState in handler Single re-render per batch
Starvation Prevention Low priority eventually executes Transitions complete when idle No infinite deferral
Time Slicing Split work into chunks Render 1000s of items without blocking Browser remains responsive

Example: Priority comparison in action

import { useState, useTransition, startTransition } from 'react';

function PriorityDemo() {
  const [urgentCount, setUrgentCount] = useState(0);
  const [transitionCount, setTransitionCount] = useState(0);
  const [isPending, startTransition] = useTransition();
  
  const handleClick = () => {
    // Urgent update - executes immediately
    setUrgentCount(c => c + 1);
    
    // Transition update - can be interrupted
    startTransition(() => {
      setTransitionCount(c => c + 1);
    });
    
    // If user clicks rapidly, urgent updates always go through
    // but transition updates may be skipped/batched
  };
  
  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      
      {/* Always up-to-date */}
      <div>Urgent: {urgentCount}</div>
      
      {/* May lag behind during rapid clicks */}
      <div style={{ opacity: isPending ? 0.5 : 1 }}>
        Transition: {transitionCount}
      </div>
    </div>
  );
}

// Practical example: Search with priority
function SearchWithPriority() {
  const [input, setInput] = useState('');
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleChange = (value) => {
    // Urgent: Input stays responsive
    setInput(value);
    
    // Transition: Expensive search update
    startTransition(() => {
      setQuery(value);
    });
  };
  
  return (
    <div>
      {/* Always responsive */}
      <input 
        value={input}
        onChange={(e) => handleChange(e.target.value)}
      />
      
      {/* Updates in background */}
      <ExpensiveSearchResults query={query} isPending={isPending} />
    </div>
  );
}

Example: Multiple priority levels in complex UI

import { useState, useTransition, useEffect } from 'react';

function Dashboard() {
  // Urgent state - user interactions
  const [selectedTab, setSelectedTab] = useState('overview');
  const [sidebarOpen, setSidebarOpen] = useState(true);
  
  // Transition state - heavy renders
  const [chartData, setChartData] = useState([]);
  const [tableData, setTableData] = useState([]);
  const [isPending, startTransition] = useTransition();
  
  // Deferred/background state - analytics
  const [analytics, setAnalytics] = useState({});
  
  const switchTab = (tab) => {
    // Urgent: Tab highlights immediately
    setSelectedTab(tab);
    
    // Transition: Heavy data processing
    startTransition(() => {
      const processed = processHeavyData(tab);
      setChartData(processed.charts);
      setTableData(processed.tables);
    });
    
    // Deferred: Track analytics (lowest priority)
    setTimeout(() => {
      setAnalytics(prev => ({
        ...prev,
        [tab]: (prev[tab] || 0) + 1
      }));
    }, 0);
  };
  
  return (
    <div>
      {/* Urgent updates - always instant */}
      <Tabs 
        active={selectedTab} 
        onSelect={switchTab} 
      />
      
      <button onClick={() => setSidebarOpen(!sidebarOpen)}>
        Toggle Sidebar
      </button>
      
      {/* Transition updates - may be delayed */}
      <div style={{ opacity: isPending ? 0.7 : 1 }}>
        <Charts data={chartData} />
        <Table data={tableData} />
      </div>
      
      {/* Background analytics - invisible to user */}
      <AnalyticsTracker data={analytics} />
    </div>
  );
}
Note: React 18's priority system ensures user interactions always feel responsive. Mark expensive updates as transitions, keep direct user feedback (clicks, typing) as urgent. The scheduler handles the rest automatically.

6. Streaming SSR and State Hydration

Feature Description Benefits Considerations
Streaming SSR Send HTML in chunks as components render Faster TTFB, progressive page load Requires React 18 + supporting framework
Selective Hydration Hydrate components on-demand Interactive sooner, prioritize visible content User interactions trigger hydration
Suspense SSR <Suspense> works on server Stream fallback, replace when ready Slow components don't block entire page
Progressive Hydration Hydrate critical parts first Above-fold content interactive immediately Below-fold waits until needed
State Serialization Serialize server state to HTML Client picks up where server left off Avoid hydration mismatches
Hydration Mismatch Server/client HTML differs Console warnings, potential bugs Use same data, avoid client-only code in render
useId const id = useId() Generate stable IDs for SSR Prevents mismatch from random IDs
Partial Hydration Some components remain static Save JS bundle size and hydration time Mark non-interactive content as static

Example: Streaming SSR with Suspense boundaries

import { Suspense } from 'react';

// Server-side rendering with streaming
function App() {
  return (
    <html>
      <body>
        {/* Critical content - renders first */}
        <Header />
        <Navigation />
        
        {/* Slow component - streams later */}
        <Suspense fallback={<SkeletonComments />}>
          <Comments /> {/* Fetches data on server */}
        </Suspense>
        
        {/* Another slow component */}
        <Suspense fallback={<SkeletonRecommendations />}>
          <Recommendations />
        </Suspense>
        
        <Footer />
      </body>
    </html>
  );
}

// Component with async data fetching
async function Comments() {
  const comments = await fetchComments(); // Suspends
  
  return (
    <div>
      {comments.map(c => (
        <Comment key={c.id} data={c} />
      ))}
    </div>
  );
}

// Streaming sequence:
// 1. Server sends: Header, Nav, SkeletonComments, SkeletonRecs, Footer
// 2. Page visible immediately with skeletons
// 3. Comments finish -> streamed, replace skeleton
// 4. Recommendations finish -> streamed, replace skeleton
// 5. Progressive hydration makes interactive on-demand

Example: Avoiding hydration mismatches

import { useState, useEffect, useId } from 'react';

// ❌ WRONG - Hydration mismatch
function BadComponent() {
  // Different on server vs client!
  const timestamp = Date.now();
  const random = Math.random();
  
  return (
    <div>
      <div>Time: {timestamp}</div>
      <div>Random: {random}</div>
    </div>
  );
}

// ✅ CORRECT - Same on server and client
function GoodComponent() {
  const [timestamp, setTimestamp] = useState(null);
  const [random, setRandom] = useState(null);
  
  useEffect(() => {
    // Client-only values set after hydration
    setTimestamp(Date.now());
    setRandom(Math.random());
  }, []);
  
  return (
    <div>
      {timestamp && <div>Time: {timestamp}</div>}
      {random && <div>Random: {random}</div>}
    </div>
  );
}

// ❌ WRONG - Random ID causes mismatch
function BadFormField() {
  const id = 'field-' + Math.random();
  return (
    <div>
      <label htmlFor={id}>Name:</label>
      <input id={id} />
    </div>
  );
}

// ✅ CORRECT - useId generates stable IDs
function GoodFormField() {
  const id = useId();
  return (
    <div>
      <label htmlFor={id}>Name:</label>
      <input id={id} />
    </div>
  );
}

// ❌ WRONG - Conditional rendering based on client-only API
function BadResponsive() {
  const isMobile = window.innerWidth < 768; // window undefined on server!
  return isMobile ? <MobileView /> : <DesktopView />;
}

// ✅ CORRECT - Handle SSR gracefully
function GoodResponsive() {
  const [isMobile, setIsMobile] = useState(false);
  
  useEffect(() => {
    setIsMobile(window.innerWidth < 768);
    
    const handler = () => setIsMobile(window.innerWidth < 768);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  
  // Server renders desktop, hydrates correctly
  return isMobile ? <MobileView /> : <DesktopView />;
}

Example: State serialization for SSR

// Server-side: Serialize initial state
import { renderToString } from 'react-dom/server';

async function handleRequest(req, res) {
  const initialData = await fetchDataForPage(req.url);
  
  const html = renderToString(<App initialData={initialData} />);
  
  const fullHtml = `
    <!DOCTYPE html>
    <html>
      <head><title>My App</title></head>
      <body>
        <div id="root">${html}</div>
        <script>
          window.__INITIAL_DATA__ = ${JSON.stringify(initialData)};
        </script>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `;
  
  res.send(fullHtml);
}

// Client-side: Hydrate with same data
import { hydrateRoot } from 'react-dom/client';

const initialData = window.__INITIAL_DATA__;
const root = document.getElementById('root');

hydrateRoot(root, <App initialData={initialData} />);

// Component uses serialized data
function App({ initialData }) {
  const [data, setData] = useState(initialData);
  
  // Both server and client render same initial content
  return (
    <div>
      {data.items.map(item => (
        <Item key={item.id} data={item} />
      ))}
    </div>
  );
}
Warning: Hydration mismatches cause React to discard server HTML and re-render on client (expensive). Avoid Date.now(), Math.random(), window/document in render. Use useEffect for client-only code and useId for stable IDs.

Section 16 Key Takeaways

  • useTransition - Keep input responsive during expensive updates, mark non-urgent updates as transitions
  • useDeferredValue - Defer expensive renders based on prop values, similar to debouncing
  • Concurrent rendering - Renders can pause/resume, keep renders pure, no side effects
  • useSyncExternalStore - Prevent tearing when integrating external stores in concurrent mode
  • Priority system - Urgent updates (clicks, typing) interrupt transitions (searches, filtering)
  • Streaming SSR - Progressive HTML delivery, selective hydration, avoid hydration mismatches