{"slug":"react-advanced-patterns","title":"React Advanced Patterns: Compound Components, Render Props, and Performance","tags":["react","patterns","performance","hooks","typescript"],"agent_summary":"Production React patterns including compound components, render props, custom hooks, memoization strategy, and React 18 concurrent features.","trigger_phrases":["React compound components","render props","useOptimistic","React performance","useMemo vs useCallback","React 18 concurrent"],"runnable":false,"markdown":"\n## Overview\n\nAdvanced React patterns for production applications — beyond the basics of `useState` and `useEffect`.\n\n## Compound Components Pattern\n\nExposes a family of related components that share implicit state through context:\n\n```tsx\n// Context holds shared state\nconst TabsContext = createContext<TabsContextType | null>(null);\n\nfunction Tabs({ children, defaultTab }: TabsProps) {\n  const [activeTab, setActiveTab] = useState(defaultTab);\n  return (\n    <TabsContext.Provider value={{ activeTab, setActiveTab }}>\n      <div className=\"tabs\">{children}</div>\n    </TabsContext.Provider>\n  );\n}\n\nfunction Tab({ id, children }: TabProps) {\n  const ctx = useContext(TabsContext)!;\n  return (\n    <button\n      className={ctx.activeTab === id ? \"active\" : \"\"}\n      onClick={() => ctx.setActiveTab(id)}\n    >\n      {children}\n    </button>\n  );\n}\n\n// Attach as static properties\nTabs.Tab = Tab;\nTabs.Panel = TabPanel;\n```\n\nUsage: `<Tabs defaultTab=\"a\"><Tabs.Tab id=\"a\">Profile</Tabs.Tab></Tabs>`\n\n## Custom Hooks — Composition Over Inheritance\n\nExtract stateful logic into reusable hooks:\n\n```tsx\nfunction useAsync<T>(asyncFn: () => Promise<T>, deps: unknown[]) {\n  const [state, setState] = useState<{\n    data: T | null;\n    error: Error | null;\n    loading: boolean;\n  }>({ data: null, error: null, loading: true });\n\n  useEffect(() => {\n    setState(s => ({ ...s, loading: true }));\n    asyncFn()\n      .then(data => setState({ data, error: null, loading: false }))\n      .catch(error => setState({ data: null, error, loading: false }));\n  }, deps);\n\n  return state;\n}\n```\n\n## Memoization Decision Matrix\n\n| Situation | Use | Why |\n|-----------|-----|-----|\n| Expensive calculation | `useMemo` | Skip recalculation |\n| Callback passed to child component | `useCallback` | Stable reference |\n| Complex child that re-renders often | `React.memo` | Skip re-render |\n| Simple primitive value | Nothing | Memoization has overhead too |\n\n**Rule:** Profile first. `React.memo` only pays off when the render is actually expensive or the component is deep in the tree.\n\n## React 18 Concurrent Features\n\n### useTransition — Non-Urgent Updates\n\n```tsx\nconst [isPending, startTransition] = useTransition();\n\nfunction handleSearch(query: string) {\n  setInputValue(query);        // Urgent: update input immediately\n  startTransition(() => {\n    setSearchResults(query);   // Non-urgent: can be interrupted\n  });\n}\n```\n\n### useDeferredValue — Defer Derived State\n\n```tsx\nconst deferredQuery = useDeferredValue(query);\n// deferredQuery lags behind query — prevents blocking the input\nconst results = useMemo(() => search(deferredQuery), [deferredQuery]);\n```\n\n### useOptimistic — Instant UI Updates\n\n```tsx\n\"use client\";\nconst [optimisticTodos, addOptimistic] = useOptimistic(\n  todos,\n  (state, newTodo: Todo) => [...state, newTodo]\n);\n\nasync function addTodo(formData: FormData) {\n  const todo = { id: crypto.randomUUID(), title: formData.get(\"title\") as string };\n  addOptimistic(todo);      // Instant UI update\n  await createTodo(todo);   // Server action (actual save)\n}\n```\n\n## State Management Decision Matrix\n\n| Scope | Tool | When |\n|-------|------|------|\n| Local UI state | `useState` | Form inputs, toggles, modals |\n| Complex local state | `useReducer` | State machine, many related fields |\n| Cross-component | Context | Theme, auth, locale (low-frequency updates) |\n| Server state | React Query / SWR | API data, caching, background refetch |\n| Global client state | Zustand | Complex app state, persisted preferences |\n\n## Performance Checklist\n\n- Keys on list items are stable IDs, never array indexes\n- `useEffect` dependencies are correct and complete\n- Client components are pushed as low as possible in the tree\n- Images use `loading=\"lazy\"` or `<Image>` (Next.js)\n- Heavy libraries are dynamically imported: `const Chart = dynamic(() => import('./Chart'))`\n","html":"<h2>Overview</h2>\n<p>Advanced React patterns for production applications — beyond the basics of <code>useState</code> and <code>useEffect</code>.</p>\n<h2>Compound Components Pattern</h2>\n<p>Exposes a family of related components that share implicit state through context:</p>\n<pre><code class=\"language-tsx\">// Context holds shared state\nconst TabsContext = createContext&#x3C;TabsContextType | null>(null);\n\nfunction Tabs({ children, defaultTab }: TabsProps) {\n  const [activeTab, setActiveTab] = useState(defaultTab);\n  return (\n    &#x3C;TabsContext.Provider value={{ activeTab, setActiveTab }}>\n      &#x3C;div className=\"tabs\">{children}&#x3C;/div>\n    &#x3C;/TabsContext.Provider>\n  );\n}\n\nfunction Tab({ id, children }: TabProps) {\n  const ctx = useContext(TabsContext)!;\n  return (\n    &#x3C;button\n      className={ctx.activeTab === id ? \"active\" : \"\"}\n      onClick={() => ctx.setActiveTab(id)}\n    >\n      {children}\n    &#x3C;/button>\n  );\n}\n\n// Attach as static properties\nTabs.Tab = Tab;\nTabs.Panel = TabPanel;\n</code></pre>\n<p>Usage: <code>&#x3C;Tabs defaultTab=\"a\">&#x3C;Tabs.Tab id=\"a\">Profile&#x3C;/Tabs.Tab>&#x3C;/Tabs></code></p>\n<h2>Custom Hooks — Composition Over Inheritance</h2>\n<p>Extract stateful logic into reusable hooks:</p>\n<pre><code class=\"language-tsx\">function useAsync&#x3C;T>(asyncFn: () => Promise&#x3C;T>, deps: unknown[]) {\n  const [state, setState] = useState&#x3C;{\n    data: T | null;\n    error: Error | null;\n    loading: boolean;\n  }>({ data: null, error: null, loading: true });\n\n  useEffect(() => {\n    setState(s => ({ ...s, loading: true }));\n    asyncFn()\n      .then(data => setState({ data, error: null, loading: false }))\n      .catch(error => setState({ data: null, error, loading: false }));\n  }, deps);\n\n  return state;\n}\n</code></pre>\n<h2>Memoization Decision Matrix</h2>\n<p>| Situation | Use | Why |\n|-----------|-----|-----|\n| Expensive calculation | <code>useMemo</code> | Skip recalculation |\n| Callback passed to child component | <code>useCallback</code> | Stable reference |\n| Complex child that re-renders often | <code>React.memo</code> | Skip re-render |\n| Simple primitive value | Nothing | Memoization has overhead too |</p>\n<p><strong>Rule:</strong> Profile first. <code>React.memo</code> only pays off when the render is actually expensive or the component is deep in the tree.</p>\n<h2>React 18 Concurrent Features</h2>\n<h3>useTransition — Non-Urgent Updates</h3>\n<pre><code class=\"language-tsx\">const [isPending, startTransition] = useTransition();\n\nfunction handleSearch(query: string) {\n  setInputValue(query);        // Urgent: update input immediately\n  startTransition(() => {\n    setSearchResults(query);   // Non-urgent: can be interrupted\n  });\n}\n</code></pre>\n<h3>useDeferredValue — Defer Derived State</h3>\n<pre><code class=\"language-tsx\">const deferredQuery = useDeferredValue(query);\n// deferredQuery lags behind query — prevents blocking the input\nconst results = useMemo(() => search(deferredQuery), [deferredQuery]);\n</code></pre>\n<h3>useOptimistic — Instant UI Updates</h3>\n<pre><code class=\"language-tsx\">\"use client\";\nconst [optimisticTodos, addOptimistic] = useOptimistic(\n  todos,\n  (state, newTodo: Todo) => [...state, newTodo]\n);\n\nasync function addTodo(formData: FormData) {\n  const todo = { id: crypto.randomUUID(), title: formData.get(\"title\") as string };\n  addOptimistic(todo);      // Instant UI update\n  await createTodo(todo);   // Server action (actual save)\n}\n</code></pre>\n<h2>State Management Decision Matrix</h2>\n<p>| Scope | Tool | When |\n|-------|------|------|\n| Local UI state | <code>useState</code> | Form inputs, toggles, modals |\n| Complex local state | <code>useReducer</code> | State machine, many related fields |\n| Cross-component | Context | Theme, auth, locale (low-frequency updates) |\n| Server state | React Query / SWR | API data, caching, background refetch |\n| Global client state | Zustand | Complex app state, persisted preferences |</p>\n<h2>Performance Checklist</h2>\n<ul>\n<li>Keys on list items are stable IDs, never array indexes</li>\n<li><code>useEffect</code> dependencies are correct and complete</li>\n<li>Client components are pushed as low as possible in the tree</li>\n<li>Images use <code>loading=\"lazy\"</code> or <code>&#x3C;Image></code> (Next.js)</li>\n<li>Heavy libraries are dynamically imported: <code>const Chart = dynamic(() => import('./Chart'))</code></li>\n</ul>\n"}