起こったこと
React Compilerを有効にした上で、以下のような ClientComponent コンポーネントをrenderすると、useMemo フックを呼び出している行で ReferenceError: func is not defined というエラーが発生する。
import { useMemo } from 'react'; function createHook(func: (x: string) => string) { const useData = () => { return useMemo(() => ['123'].map((x) => func(x)), []); // ReferenceError: func is not defined }; return useData; } const useSomeData = createHook((x) => x); export function ClientComponent() { const data = useSomeData(); return <h1>Hello {data.join(' ')}</h1>; }
React Compiler Playgroundに投げてみると、確かに生成されるコードがおかしい。以下のような関数が末尾で定義されているが、func 変数をうまく引き回せていない。
function _temp(x) { return func(x); }
createHook 関数は、与えられた引数をもとにReactフックを作成して返す関数である。ロジックを共通化する目的で、フックを生成する関数を用意する実装パターンを実践している。実際に不具合を踏み抜いたコードはもうちょっと長く、最小の再現コードを作ったらこのような形に落ち着いた。
この実装パターンがReactのルールに反していたらこっそり教えてください。
原因
おそらくReact Compilerの既知の不具合。
回避方法
useData 関数の内部に use no memo ディレクティブを書いて、React Compilerによる最適化を行わないようにすることで回避できる。
function createHook(func: (x: string) => string) { const useData = () => { 'use no memo'; return useMemo(() => ['123'].map((x) => func(x)), []); }; return useData; }
今回の場合だと useData 関数をarrow functionではなく function 宣言で定義することでも回避できた。なんでこれで回避できるのかは不明。
function createHook(func: (x: string) => string) { function useData() { return useMemo(() => ['123'].map((x) => func(x)), []); }; return useData; }
しかしReact Compilerって夢みたいな技術でいいですよね。あとReact Compiler Playgroundの存在はissueを見にいって知りました。