React Compiler 導入時は react-hook-form / TanStack 系のコンポーネントに 'use no memo'
React Compiler は「props や state の参照が同じなら結果も同じ」と見なして自動メモ化する。参照を保ったまま中身を変えるライブラリ(react-hook-form、TanStack Query の useInfiniteQuery、TanStack Table)を使うコンポーネントでは更新が画面に反映されなくなるので、そのファイルだけ除外する。
導入手順(Vite + React 19)
sh
pnpm add -D babel-plugin-react-compiler
pnpm dlx react-compiler-healthcheck --src 'src/**/*.{ts,tsx}' # 事前に互換性を確認ts
// vite.config.ts — 環境変数ですぐ切り戻せるようにしておく
const enableReactCompiler = process.env.REACT_COMPILER !== 'false'
export default defineConfig({
plugins: [
react({
babel: enableReactCompiler ? { plugins: ['babel-plugin-react-compiler'] } : undefined,
}),
],
})React DevTools のコンポーネントツリーに「Memo」バッジが付けば効いている。
起きた症状
healthcheck は全コンポーネント成功だったのに、結合テストで次が見つかった:
- react-hook-form: バリデーションエラーが出ない、入力が画面に反映されない、
formState.isDirtyが変わらない(formStateは Proxy で追跡しており、formオブジェクトの参照は変わらないため)。 useInfiniteQuery: 「もっと読む」でdata.pagesが増えても再描画されないことがある。useReactTable: ソートやページ送りが反映されない。
対処
tsx
'use no memo' // ファイル先頭。このファイルのコンポーネントを自動メモ化の対象外にする
import { useForm } from 'react-hook-form'
export const MyForm = () => {
const form = useForm()
return <form>{/* ... */}</form>
}付ける対象の目安:
useForm(やそれを包むカスタムフック)を呼ぶ、またはformを props で受け取るコンポーネント- フォームを作って子に渡す親コンポーネント
useInfiniteQueryのデータに依存するコンポーネントuseReactTableを使うコンポーネント
カスタムフック(useZodForm.tsなど)自体には不要。使う側のコンポーネントに付ける。
効果の例(元記事の計測)
一覧画面の再レンダリング時間が 47.6ms → 3.58ms(約93%減)、バンドルは gzip で +約12KB(+3.7%)。
注意
- この種の不具合はビルド時に分からず実行時にしか出ない。導入前に操作を伴う結合テストを用意しておく。
- ライブラリ側の対応が進めば不要になる可能性がある。バージョンを上げたら外して試す。
出典
- @kakehashi_dev 2026-03-24: @kakehashi_dev · 2026-03-24
- 元記事: kakehashi-dev.hatenablog.com