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

React Compiler導入から21ヶ月、いま始めるならこうやる

React Compiler導入から21ヶ月、いま始めるならこうやる

Avatar for Tatsuya Asami

Tatsuya Asami

May 16, 2026

More Decks by Tatsuya Asami

Other Decks in Technology

Transcript

  1. 自己紹介 浅⾒ 達也 (@astatsuya1) • 2024年7⽉頃にexperimental版だったReact Compilerを有効にしました ◦ ZennでReact Compilerを有効にして9ヶ⽉が経ちましたを書きました

    ▪ https://zenn.dev/dev_commune/articles/e42847c9ce3c97 ▪ 正式版リリース前の話なので参考程度にして下さい • コミューン株式会社 プラットフォームエンジニアリング本部 本部⻑ ◦ ⾊々やるが私はフロントエンドが得意
  2. © Commune Inc. All rights reserved ⽬次 1. React Compilerとは?

    2. 導⼊時におきたこと 3. 導⼊後の変化 4. これから導⼊するなら 5. まとめ
  3. React Compilerとは? • React アプリを⾃動的に最適化する新しいビルド時ツールです。 ◦ https://ja.react.dev/learn/react-compiler/introduction • React Compilerを使わない場合はuseCallback、useMemo,

    React.memo等 を使って⼿動でメモ化する必要がある • React Compilerを使うと⾃動でメモ化してくれる • babel-plugin-react-compiler v1.0.0が2025年10⽉にリリースされた ◦ 2026年5⽉14⽇現在もv1.0.0が最新版
  4. React Compilerとは? // React Compilerを使用しない場合 import { useMemo, useCallback, memo

    } from 'react'; const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) { const processedData = useMemo(() => expensiveProcessing(data), [data]); const handleClick = useCallback((item) => onClick(item.id), [onClick]); return ( <div> {processedData.map((item) => ( // onClickに渡しているアロー関数はレンダーごとに新しく作られるため、handleClickをメモ化してもItemのメモ化は効かない <Item key={item.id} onClick={() => handleClick(item)} /> ))} </div> ); }); 出典:https://ja.react.dev/learn/react-compiler/introduction#before-react-compiler
  5. React Compilerとは? // React Compilerを使用する場合 function ExpensiveComponent({ data, onClick })

    { // React.memoは不要 const processedData = expensiveProcessing(data); // useMemoは不要 const handleClick = (item) => onClick(item.id); // useCallbackは不要 return ( <div> {processedData.map((item) => ( // onClickに渡しているアロー関数はitemが変わらない限り同じ関数が使い回されるため、Itemのメモ化が効く <Item key={item.id} onClick={() => handleClick(item)} /> ))} </div> ); } 出典:https://ja.react.dev/learn/react-compiler/introduction#after-react-compiler
  6. 導入時におきたこと なぜReact Compilerがexperimental版の段階で導⼊したのか? • データ周りの⼤きなアップデートがあり全機能テストをしないといけな かった • どうせ全機能をテストするなら後々変更するのが怖いことをやっておきた かった ◦

    react19, next15へのアップデートと、react-compilerの導⼊をやった ◦ いずれもソースコードの変更量は少なく、リリース前に問題があった らダウングレードや無効化で撤退できそうだった
  7. 導入時におきたこと どんな感じだったか • ⼤体は期待通りメモ化されそのまま動いた! • ⼀部動かない箇所は頑張って書き換え、時間がかかりそうな箇所はuse no memoディレクティブ をつけた ◦

    1ページだけ。5コンポーネントくらいにつけた • 全てのuseCallbackやuseMemo等を外したわけではない ◦ useCallbackを外すとuseEffectが無限ループする箇所があった ◦ 撤退も選択肢に残しておきたかったので、触る必要のないコードはそのままにしていた • そんなことがありながら無事React Compilerを有効にした状態でリリース
  8. 導入後の変化 • とても良かった ◦ メモ化のことを気にせず開発できるようになったこと ▪ 考えることが減ってその分他のことに集中できるようになった ◦ 記述量が減ってコードが読みやすくなった •

    そうでもなかった ◦ ユーザー⽬線で嬉しいことはなさそうだった ▪ 確かに多くのコンポーネントはメモ化されてパフォーマンスは上がって いた ▪ おそらくどれも数10msの差で、⾔われなければ気が付かない程度
  9. 今からやる場合の導入手順 1. eslint-plugin-react-hooksを⼊れて違反を直す • React公式が提供しているリンター • hooksだけでなくReact全般のルール • 元々React Compilerのリンターはeslint-plugin-react-compilerだったが、

    React Compilerの安定版が公開されたタイミングで統合された ◦ https://react.dev/blog/2025/10/07/react-compiler-1#migrating-from-eslint-plugin-reac t-compiler-to-eslint-plugin-react-hooks
  10. 今からやる場合の導入手順 1. eslint-plugin-react-hooksを⼊れて違反を直す • eslint-plugin-react-hooksはbabel-plugin-react-compilerと同じチェック ロジックを使っている ◦ リントで多くの違反を事前に検出出来る(※ただし全てではない) • React

    Compiler は最適化すると壊れそうなコードを⾒つけた場合に、最適 化をスキップしてくれる事がある ◦ スキップ対象は重⼤度がError相当の場合のみ。Warning以下はスキッ プされない(最適化されると壊れる可能性がある)
  11. 今からやる場合の導入手順 1. eslint-plugin-react-hooksを⼊れて違反を直す • つまり違反だらけの状態で有効にしても最適化の効果は薄いし、動かなくなる可 能性もある • oxlintでもjsPluginsを使えばチェックできる ◦ https://oxc.rs/docs/guide/usage/linter/plugins.html#supported-plugins

    ここの下の⽅にあるリ ンク先のissueに設定⽅法が書いてある • biome等別のリンターを使っていて、リンターの併⽤をしたくない場合でも、⼀ 度違反がないか検出してみるのをおすすめ → まずはeslint-plugin-react-hooksで違反がない状態を⽬指しましょう
  12. 今からやる場合の導入手順 2. React Hook Formの使い⽅を⾒直す • React Compiler関係なく従う必要のあるルールがある。例えば、 ◦ formStateはレンダリングフェーズでプロパティへのアクセスを発⽣させる

    必要がある ▪ https://react-hook-form.com/docs/useform/formstate ◦ リアクティブに値を更新したい箇所ではuseWatchを使い、⼀度だけ使いた い時はgetValuesを使う ※ 詳しい説明は省略させていただきます。React Hook Formの内部実装に依存しているので今後変わる可能性 もあります。
  13. 今からやる場合の導入手順 2. React Hook Formの使い⽅を⾒直す • 前ページのようなReact Hook Formのルールに加えて、React Compilerと

    併⽤する場合は下記のルールも追加する ◦ watchは使わずuseWatchを使う ◦ formStateは使わずuseFormStateを使う ◦ resetとuseFormの引数のvaluesはregisterを使っていると動かないの で、controllerを使う ※私の経験上はこれで⼤丈夫ですが、他に動かないパターンもあるかもしれません
  14. 今からやる場合の導入手順 2. React Hook Formの使い⽅を⾒直す • React Hook FormとReact Compilerの挙動をテストしている⼈がいたの

    で、 そのコードを利⽤させてもらい2026年5⽉時点の最新バージョンに上 げて試してみたところ、前ページのルールに従っているテストは全て通り ました ◦ https://github.com/timkindberg/rhf-compiler-compat (PRマージしてもらった) ◦ 以前は半分くらいのテストが落ちていた
  15. 今からやる場合の導入手順 2. React Hook Formの使い⽅を⾒直す • React Hook Form +

    React Compilerのリンターを使う ◦ eslint-plugin-react-hooksのincompatible-libraryでuseFormの戻り値の watchはWarningを出してくれる ▪ ただし、useFormContextの戻り値のwatchはチェックしてくれない ◦ eslint-plugin-react-hook-formというのもある ▪ formStateやuseFormContextの戻り値のwatchもチェックしてくれる
  16. 今からやる場合の導入手順 3. React Compilerが有効な状態で⾃動テストを実⾏する • babel/jestはpluginsにbabel-plugin-react-compilerを追加すればOK • bun testはtransform処理を噛まして動いていた ◦

    https://github.com/timkindberg/rhf-compiler-compat/blob/main/compiler-plugin.ts • 私の環境のnext/jestはjestの複雑なtransform処理の追加が必要でした ◦ ⾊々な要素が絡んで難しかった ◦ AIが作ってくれたが正直何をやっているのかわからない
  17. 今からやる場合の導入手順 // jest.config.compiler.js const nextJest = require('next/jest' ) const swc

    = require('@swc/core' ) const babel = require('@babel/core' ) // === Transformer: SWC → Babel の 2 段構成 === const reactCompilerTransformer = { process(src, filename) { // ① SWC: TS strip するが JSX は残す(runtime: 'preserve' ) const swcResult = swc.transformSync (src, { filename, jsc: { parser : { syntax: 'typescript' , tsx: true }, transform : { react: { runtime : 'preserve' } } }, module : { type: 'es6' } }) // ② Babel: JSX 変換 + React Compiler + CJS 化 const babelResult = babel.transformSync (swcResult.code, { filename, presets : [['next/babel' , { 'preset-react' : { runtime : 'automatic' } }]], plugins : [['babel-plugin-react-compiler' , { target : '19' }]] }) return { code: babelResult .code } } } // === Jest 設定: next/jest の transform を差し替える === const createJestConfig = nextJest({ dir: __dirname }) module.exports = async () => { const base = await createJestConfig ({})() return { ...base, transform : { ...base.transform, '^.+\\.(js|jsx|ts|tsx|mjs)$' : reactCompilerTransformer } } } こんなイメージ。実際はもっと⾊々やっていて132⾏になった。
  18. 今からやる場合の導入手順 4. 段階的に有効にする • 部分的に無効、部分的に有効どちらも出来る ◦ compilationMode: infer + “use

    no memo”ディレクティブで部分的に無効にする ◦ compilationMode: annotation + “use memo”ディレクティブで部分的に有効にす る • ディレクティブ以外でもゲーティングを使って制御出来る ◦ https://ja.react.dev/learn/react-compiler/incremental-adoption#runtime-feature-flags-with-gating ◦ Next.jsの場合だとこのオプションは使えない様⼦
  19. まとめ • React Compilerを有効にすることで、メモ化する、しないを頭から切り離 す事が出来て⾮常に良かった • 今ならReact Hook Formも⼀定のルールに従えばReact Compilerと共存出

    来る可能性が⾼い • eslint-plugin-react-hooksはReactの教科書 ◦ React Compilerを有効にするつもりがない⼈もルールを読んでみると ためになる