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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for seriseri seriseri
June 12, 2026
39

アンチパターンを避ける型駆動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