Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アンチパターンを避ける型駆動React最適化 (TSKaigi2026 アフターパーティー)

Avatar for seriseri seriseri
June 12, 2026
65

アンチパターンを避ける型駆動React最適化 (TSKaigi2026 アフターパーティー)

Avatar for seriseri

seriseri

June 12, 2026

Transcript

  1. © PeopleX Inc. 3 アジェンダ 1. React Compilerとは 2. React

    Compilerの仕組み 3. 最適化を阻害す アンチパターン 4. 対策
  2. © PeopleX Inc. 5 React Compilerとは 5 // “とりあえず全部メモ化” 典型

    const Row = memo((props) => { const label = useMemo(() => formatLabel(props.user), [props.user]); const onClick = useCallback(() => { props.onSelect(props.user.id); }, [props.user.id, props.onSelect]); return <li onClick={onClick}>{label}</li>; }); ⼿動メモ化のコスト • 依存配列の管理コスト • メモ化漏 、誤メモ化 • レビューコスト • バンドルサイズの肥⼤化
  3. © PeopleX Inc. 6 React Compilerとは ⼿動メモ化 • useMemo •

    useCallback • React.memo © PeopleX Inc. 6 こ まで React Compiler 爆誕 コンパイル時の⾃動メモ化 • useMemo • useCallback • React.memo こ から Before • 依存配列の正確さがチームに依存 • 再レンダリングが設計、レビューの中⼼ After • 誰でも • 設計中⼼は責務分離と副作⽤の隔離 React Compilerの登場
  4. © PeopleX Inc. 8 React Compilerとは © PeopleX Inc. 8

    どんなコンポーネントでも 最適化してく ??
  5. © PeopleX Inc. 10 React Compilerとは © PeopleX Inc. 10

    「純粋」なコンポーネントだけ 最適化(キャッシュ化)してく
  6. © PeopleX Inc. 11 React Compilerとは © PeopleX Inc. 11

    「純粋」=  同じ props を渡せ 必ず同じ結果を返し、レン ダー中に外部 状態を一切書き換えない(副作 用を持たない)コンポーネント こと
  7. © PeopleX Inc. 13 React Compilerの仕組み コンパイルのパイプライン(実⾏順序) 1. Parse(JSX /

    TS → AST) — コードを構⽂⽊に分解す 2. 中間表現の構築 — 解析しやすい中間表現に整え 3. データフロー解析 — 値の依存関係を洗い出す 4. 副作⽤ / 可変性の推論 — 副作⽤がない範囲を⾒極め 5. メモ化境界の挿⼊ — 安全な範囲をキャッシュ化す © PeopleX Inc. 13
  8. © PeopleX Inc. 14 React Compilerの仕組み © PeopleX Inc. 14

    」 FunctionDeclaration UserList ├─ params: [ ObjectPattern { users, query } ] └─ body: BlockStatement ├─ VariableDeclarator filtered │ └─ CallExpression users.filter( ArrowFn ) ├─ VariableDeclarator count │ └─ MemberExpression filtered.length └─ ReturnStatement └─ JSXElement <List …/> function UserList({ users, query }) { const filtered = users.filter(u => u.name.includes(query)); const count = filtered.length; return <List items={filtered} total={count} />; } 1. Parse(JSX / TS → AST) — コードを構⽂⽊に分解す ⼊⼒コンポーネント 中間データ:AST(構⽂⽊)
  9. © PeopleX Inc. 15 React Compilerの仕組み © PeopleX Inc. 15

    2. 中間表現の構築 — 解析しやすい中間表現に整え 」 bb0 (entry): [1] $0 = LoadProp users.filter [2] $1 = FunctionExpr λ(u) [3] $2 = Call $0($1) ; → filtered [4] $3 = LoadProp $2.length ; → count [5] $4 = JSX <List items=$2 total=$3/> [6] Return $4 中間データ:HIR(SSA / basic blocks) function UserList({ users, query }) { const filtered = users.filter(u => u.name.includes(query)); const count = filtered.length; return <List items={filtered} total={count} />; } ⼊⼒コンポーネント
  10. © PeopleX Inc. 16 React Compilerの仕組み © PeopleX Inc. 16

    」 filtered := f(users, query) deps → { users, query } count := f(filtered) deps → { filtered } jsx := f(filtered,count) deps → { users, query } // 末端 JSX users / query に み依存 function UserList({ users, query }) { const filtered = users.filter(u => u.name.includes(query)); const count = filtered.length; return <List items={filtered} total={count} />; } 3. データフロー解析 — 値の依存関係を洗い出す ⼊⼒コンポーネント 中間データ:依存グラフ(reaching defs)
  11. © PeopleX Inc. 17 React Compilerの仕組み © PeopleX Inc. 17

    」 filtered := f(users, query) deps → { users, query } count := f(filtered) deps → { filtered } jsx := f(filtered,count) deps → { users, query } // 末端 JSX users / query に み依存 function UserList({ users, query }) { const filtered = users.filter(u => u.name.includes(query)); const count = filtered.length; return <List items={filtered} total={count} />; } 3. データフロー解析 — 値の依存関係を洗い出す ⼊⼒コンポーネント 中間データ:依存グラフ(reaching defs) 入力にしか依存していないことがわかる = 純粋
  12. © PeopleX Inc. 18 React Compilerの仕組み © PeopleX Inc. 18

    「純粋」=  同じ props を渡せ 必ず同じ結果を返し、レン ダー中に外部 状態を一切書き換えない(副作 用を持たない)コンポーネント こと
  13. © PeopleX Inc. 19 React Compilerの仕組み © PeopleX Inc. 19

    」 filtered := f(users, query) deps → { users, query } count := f(filtered) deps → { filtered } jsx := f(filtered,count) deps → { users, query } // 末端 JSX users / query に み依存 function UserList({ users, query }) { const filtered = users.filter(u => u.name.includes(query)); const count = filtered.length; return <List items={filtered} total={count} />; } 3. データフロー解析 — 値の依存関係を洗い出す ⼊⼒コンポーネント 中間データ:依存グラフ(reaching defs) 入力にしか依存していないことがわかる = 純粋
  14. © PeopleX Inc. 20 」 React Compilerの仕組み © PeopleX Inc.

    20 4. 副作⽤ / 可変性の推論 — 純粋性を満たす範囲を特定 5. メモ化境界の挿⼊ const $ = _c(5); // useMemoCache let filtered; if ($[0] !== users || $[1] !== query) { filtered = users.filter(u => u.name.includes(query)); $[0]=users; $[1]=query; $[2]=filtered; } else filtered = $[2]; const count = filtered.length; let t; if ($[3] !== filtered) { t = <List items={filtered} total={count}/>; $[3]=filtered; $[4]=t; } else t = $[4]; return t; 出⼒:メモ化済みコード
  15. © PeopleX Inc. 22 コンパイラに諦めら 3つのパターン © PeopleX Inc. 22

    1. 副作⽤の混⼊ 2. ミュータブル操作 3. ⾮決定的な依存
  16. © PeopleX Inc. 23 コンパイラに諦めら 3つのパターン © PeopleX Inc. 23

    1. 副作⽤の混⼊ 2. ミュータブル操作 3. ⾮決定的な依存 」 function Total({ items }) { const total = items.reduce(...); logger.info(total); // I/O return <p>{total}</p>; }
  17. © PeopleX Inc. 24 コンパイラに諦めら 3つのパターン © PeopleX Inc. 24

    1. 副作⽤の混⼊ 2. ミュータブル操作 3. ⾮決定的な依存 」 function Profile({ user }) { user.name = user.name.trim() return <h1>{user.name}</h1>; }
  18. © PeopleX Inc. 25 コンパイラに諦めら 3つのパターン © PeopleX Inc. 25

    1. 副作⽤の混⼊ 2. ミュータブル操作 3. ⾮決定的な依存 」 function Token() { const id = Math.random(); return <p>{id}</p> }
  19. © PeopleX Inc. 27 対策 © PeopleX Inc. 27 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認 // props を deep readonly で受ける type Props = { items: ReadonlyArray<Item>; user: Readonly<User>; }; function List({ items }: Props) { items.push(...) // ✕ コンパイルエラー const next = [...items, x]; // ◦ }
  20. © PeopleX Inc. 28 コンパイラに諦めら 4つのパターン © PeopleX Inc. 28

    1. 副作⽤の混⼊ 2. ミュータブル操作 3. 参照不安定性 4. ⾮決定的な依存 」 function Profile({ user }) { user.name = user.name.trim() return <h1>{user.name}</h1>; } こ を防ぐ!
  21. © PeopleX Inc. 29 対策 © PeopleX Inc. 29 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認 // props を deep readonly で受ける type Props = { items: ReadonlyArray<Item>; user: Readonly<User>; }; function List({ items }: Props) { items.push(...) // ✕ コンパイルエラー const next = [...items, x]; // ◦ }
  22. © PeopleX Inc. 30 対策 © PeopleX Inc. 30 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認
  23. © PeopleX Inc. 31 対策 © PeopleX Inc. 31 Biome

    Rust 製 高速 Lint / Formatter • noParameterAssign — 引数 再代入を禁止 • useReadonlyClassProperties — 不変表明を強制 • noUselessFragments / noUselessRename — render を清潔に保つ Oxc Rust 製 オールインワン JS/TS ツールチェイン • oxlint react-hooks/* — フック規約を高速検査 • react_compiler 互換ルール — 副作用混入を検出 Lintで防ぐ
  24. © PeopleX Inc. 32 対策 © PeopleX Inc. 32 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認 純粋層 / 副作用層 / UI 層を物理的に分ける • 純粋層(Domain) ◦ 純粋関数 / 不変データ • 副作用層(Effects) ◦ I/O / fetch / storage / log ◦ 副作用 専用フック・サービスに隔離 • UI層 ◦ JSX / Server Components ◦ 純粋層 値をただ描画するだけ
  25. © PeopleX Inc. 33 対策 © PeopleX Inc. 33 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認 純粋層 / 副作用層 / UI 層を物理的に分ける • 純粋層(Domain) ◦ 純粋関数 / 不変データ ◦ Compiler が最適化できる純粋層 • 副作用層(Effects) ◦ I/O / fetch / storage / log ◦ 副作用 専用フック・サービスに隔離 • UI層 ◦ JSX / Server Components ◦ 純粋層 値をただ描画するだけ コンテナ・プレゼンターパターン (+hooks)
  26. © PeopleX Inc. 34 対策 © PeopleX Inc. 34 1.

    型で防ぐ 2. Lintで防ぐ 3. 設計で防ぐ 4. ランタイムでの確認
  27. 35