enableForestを使うことでReact Compilerのメモ化レベルを変更できる
Intro
先日Youtubeで公開したReact Compiler Code reading #1の中で出てきた謎オプション、enableForest
についてまとめました。
コメントが🌲です。さすがに二度見します。
6/5 追記
Xにて、React teamのJoe Savonaさんにコメントいただきました!
オプションをいじればバグが起きてしまう可能性があるので、できればデフォルトのままの方が良いとのことです!
結論
React Compilerのメモ化のレベルを変更できるオプションです。名前とコメントのせいで怪しく見えますが、意外とちゃんと使われてます。
code
以下のようなサンプルコードを用意しました。シンプルなReactのコードになっていると思います。
function Title() {
return (
<div>
<h1>My React App</h1>
<p>Welcome to my simple React application!</p>
</div>
);
}
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<h2>Counter App</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
function App() {
return (
<div>
<Title />
<Counter />
</div>
);
}
以下のようにそれぞれコードが出力されます
enableForest off
function Title() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (
<div>
<h1>My React App</h1>
<p>Welcome to my simple React application!</p>
</div>
);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
function Counter() {
const $ = _c(15);
const [count, setCount] = useState(0);
let t0;
if ($[0] !== count) {
t0 = () => {
setCount(count + 1);
};
$[0] = count;
$[1] = t0;
} else {
t0 = $[1];
}
const increment = t0;
let t1;
if ($[2] !== count) {
t1 = () => {
setCount(count - 1);
};
$[2] = count;
$[3] = t1;
} else {
t1 = $[3];
}
const decrement = t1;
let t2;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
t2 = <h2>Counter App</h2>;
$[4] = t2;
} else {
t2 = $[4];
}
let t3;
if ($[5] !== count) {
t3 = <p>Count: {count}</p>;
$[5] = count;
$[6] = t3;
} else {
t3 = $[6];
}
let t4;
if ($[7] !== increment) {
t4 = <button onClick={increment}>Increment</button>;
$[7] = increment;
$[8] = t4;
} else {
t4 = $[8];
}
let t5;
if ($[9] !== decrement) {
t5 = <button onClick={decrement}>Decrement</button>;
$[9] = decrement;
$[10] = t5;
} else {
t5 = $[10];
}
let t6;
if ($[11] !== t3 || $[12] !== t4 || $[13] !== t5) {
t6 = (
<div>
{t2}
{t3}
{t4}
{t5}
</div>
);
$[11] = t3;
$[12] = t4;
$[13] = t5;
$[14] = t6;
} else {
t6 = $[14];
}
return t6;
}
function App() {
const $ = _c(2);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <Title />;
$[0] = t0;
} else {
t0 = $[0];
}
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = (
<div>
{t0}
<Counter />
</div>
);
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
enableForest on
function Title() {
return (
<div>
<h1>My React App</h1>
<p>Welcome to my simple React application!</p>
</div>
);
}
function Counter() {
const $ = _c(4);
const [count, setCount] = useState(0);
let t0;
if ($[0] !== count) {
t0 = () => {
setCount(count + 1);
};
$[0] = count;
$[1] = t0;
} else {
t0 = $[1];
}
const increment = t0;
let t1;
if ($[2] !== count) {
t1 = () => {
setCount(count - 1);
};
$[2] = count;
$[3] = t1;
} else {
t1 = $[3];
}
const decrement = t1;
return (
<div>
<h2>Counter App</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
function App() {
return (
<div>
<Title />
<Counter />
</div>
);
}
違い
// enableForestをon
function Title() {
return (
<div>
<h1>My React App</h1>
<p>Welcome to my simple React application!</p>
</div>
);
}
// enableForestをoff
function Title() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (
<div>
<h1>My React App</h1>
<p>Welcome to my simple React application!</p>
</div>
);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
enableForestを有効にしたことでまずわかるのは、Titleコンポーネントがほぼ何も変わってないことです。
このオプションはメモ化のレベルを変更すると最初に言いましたが、jsxに関してはほぼメモ化しないようになるみたいです。
実装
↑ enableForestの値によって、memoizeJsxElements
とforceMemoizePrimitives
の値が変わっています。
↑ memoizeJsxElementsの値によって、メモ化のレベルを変えています。
このmemoizeJsxElements
は二箇所で使用されています。
-
JsxExpression
:Identifier
はComponent、JsxAttribute
がHTMLタグ -
JsxFragment
: フラグメント
↑ に対してのメモ化がなくなります。なのでほぼjsxに対してのメモ化はなくなるということですね。
あとforceMemoizePrimitives
というものもあります。これはenableForest
がtrueだったらメモ化を行います。
これは三箇所で使用されています。
-
UnaryExpression
: "void" | "throw" | "delete" | "!" | "+" | "-" | "~" | "typeof" -
PropertyLoad
: propsにオブジェクトプロパティのロード -
ComputedLoad
: props["exmaple"]みたいにアクセスしたときのロード
なんとなくそこそこ使えそうなオプションだなということが理解できますね。個人的には全てのメモ化を下げず、下げたい時に"use no memo"とか使えばいいんじゃないかなと思ったりしますが👀
まとめ
-
enableForest
オプションとは、React Compilerのメモ化レベルを変更できるオプション - memoizeJsxElementsはenableForestがtrueだったらjsxをメモ化しないようにする
- forceMemoizePrimitivesはenableForestがtrueだったら演算子やpropsのオブジェクトに対してメモ化を行う
引き続きReact Compilerのコードを見てブログ書いていきます。
Discussion