1712170082
2024-04-03 10:22:45
React 19 と、以前は React Forget として知られていた React Compiler が、ここ 1 か月間 React の議論を独占してきました。 私たちは皆、近いうちに React でのメモ化について考える必要がなくなる可能性について (良い意味で) 正気を失いつつあります。 それは本当ですか? 忘れ始めたほうがいいでしょうか memo、 useMemo、 そして useCallback 今後数か月以内に? そして、React コンパイラーがリリースされると実際に何が変わるのでしょうか? その後、React について何を学び、教えるべきでしょうか?
見てみましょう。
最も重要なことを忘れておきましょう。メモ化はすぐになくなるわけではないので、まだ学習をやめないでください。 React 19 は React コンパイラーではありません。 React チームはコンパイラーを 2016 年に発表しました。 同じブログ投稿 そこで彼らは React 19 の近日リリースを発表し、誰もが興奮して結論に飛びつきました。
しかし、React チーム メンバーのツイートがこの混乱を明らかにしています。
React 19 では、たくさんの新機能が登場しますが、コンパイラーについてはもう少し待つ必要があります。 どれくらいの期間かは現時点では明らかではありませんが、別の React コアチームメンバーの別のツイートによると、今年末までに実現する可能性があります。
個人的には、このタイムラインには懐疑的です。 の講演を見てみると、 React チームのメンバー コンパイラーとそのタイムラインを紹介しましたが、私たちはコンパイラーの旅の途中にいます。

2021年からジャーナルがスタートし、 二年前。 これと同じくらい基本的なものを Meta ほどの大規模なコードベースに展開するのは、おそらく非常に複雑です。 したがって、タイムラインの途中から最後までジャンプするにはさらに 2 年かかる可能性があります。
しかし、もしかしたら React チームが実際に今年リリースできるかもしれません。 それは良い知らせでしょう。 コンパイラの現在の約束は、 ビデオで言及された、それは、採用のためにコードを変更する必要がないということです。 それはちょうどうまくいきます™️。 実際に年末までにリリースされるのであれば、これが実際に当てはまることを示す非常に良い兆候であり、残りの人々はすぐに簡単に切り替えることができるはずです。
ただし、コンパイラーが今年リリースされ、実際に導入が非常に簡単で欠点がなかったとしても、それを忘れることができるという意味ではありません。 useCallback そして memo すぐに。 常に「移行」期間があり、最初に「すでにコンパイラを有効にしている場合」のケースについて説明しますが、その後「まれなケースですが、まだコンパイラに移行していない場合」のシナリオにゆっくりと移行します。
クラス コンポーネントからフックを備えた機能コンポーネントへの精神的な移行には、少なくとも 3 年 (2018 年から開始) かかったと思います。すべてのコース、ドキュメント、ブログが追いついたとき、ほとんどの人がフックを備えた React バージョンに移行しました。デフォルトとしての機能コンポーネントとフックについて話し始めました。 そして6年経った今でも、クラスの要素があちこちにたくさん潜んでいます。
同様のタイムラインをコンパイラーに適用すると、どのような内容の知識を保持する必要があることになります。 memo、 useMemo そして useCallback 少なくとも今後3年間は続く。 幸運にも、リリース後すぐにコンパイラーに移行できる最新のコードベースで作業できる場合は、それほど問題はありません。 あなたが React の教師であるか、大量のレガシー コードで移行に時間がかかる大規模なコードベースで働いている場合はさらに重要です。
では、具体的に何が変わっているのでしょうか? 単純化した答えは、すべてがメモ化されるということです。 React Compiler は、典型的な React コードを、すべてのフックの依存関係、コンポーネントの props、およびコンポーネント自体がメモ化されたコードに変換する Babel プラグインになります。 基本的に、このコードは次のようになります。
const Component = () => {
const onSubmit = () => {};
const onMount = () => {};
useEffect(() => {
onMount();
}, [onMount]);
return <Form onSubmit={onSubmit} />;
};
以下は両方のように動作します
onSubmitそしてonMountに包まれていますuseCallbackそしてFormに包まれていますReact.memo:const FormMemo = React.memo(Form);
const Component = () => {
const onSubmit = useCallback(() => {}, []);
const onMount = useCallback(() => {}, []);
useEffect(() => {
onMount();
}, [onMount]);
return <FormMemo onSubmit={onSubmit} />;
};
コンパイラはそれらを次のように変換しません。 その通り もちろん、そのコードはこれよりもはるかに複雑で高度です。 しかし、これは私たちがそれを理解するための良いメンタルモデルです。 詳しい内容が気になる方はこちらをオススメします このビデオを見ている コンパイラーを導入した React コアチームのメンバーから。 なぜこれを使用するのかが少し曖昧な場合は、
useCallbackそしてmemoとにかく、最初の 6 つのビデオを見ることをお勧めします。 アドバンストリアクトシリーズ ユーチューブで。 再レンダリングとメモ化に関するすべてをカバーしています。 あるいは、読書に興味がある場合は、以下をお読みください。 ここにあるすべて。私たちが React を教えたり学習したりする方法にとって、この移行はいくつかのことを意味します。
親が再レンダリングすると、子も再レンダリングされます
現在、親コンポーネントが再レンダリングされると、内部でレンダリングされるすべてのコンポーネントも同様に再レンダリングされます。
const Parent = () => {
return <Child />;
};
現在、多くの人が次のように信じています。
Childコンポーネントは、プロパティが変更された場合にのみ再レンダリングされます。 私はこれをこう呼ぶのが好きです Big Re-renders の神話。 現時点では、これは真実ではありません。 標準の React の動作では Props は重要ではありません。面白いことに、コンパイラを使用するとそれが当てはまります。 すべてが内部でメモ化されているため、現在の通説が実際には標準の React 動作になります。 数年以内に、React コンポーネントはその状態またはプロパティが変更された場合にのみ再レンダリングされ、親が再レンダリングされたかどうかは重要ではないことを教えることになるでしょう。 人生は時々奇妙です。
パフォーマンスのための作曲はもう不要
現在、次のようないくつかの合成テクニックがあります。 「状態を下に移動」 または 「コンポーネントを子として渡す」 再レンダリングを減らすことができます。 通常は、いじる前にそれらを使用することをお勧めします
useCallbackそしてmemo、 以来 React で物事を適切にメモするのは非常に困難です。たとえば、このコードでは次のようになります。
const Component = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>
open dialog
Button>
{isOpen && <ModalDialog />}
<VerySlowComponent />
>
);
};
の
VerySlowComponentダイアログが開くたびに再レンダリングされるため、ダイアログが開くのに時間がかかります。 ダイアログを開く状態を次のようにコンポーネントにカプセル化すると、次のようになります。const ButtonWithDialog = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>
open dialog
Button>
{isOpen && <ModalDialog />}
>
);
};
const Component = () => {
return (
<>
<ButtonWithDialog />
<VerySlowComponent />
>
);
};
基本的に、不必要な再レンダリングを取り除きました。
VerySlowComponent何もメモせずに。コンパイラが実用化されると、これらのパターンはパフォーマンス上不要になります。 おそらく、懸念事項の構成と分離の目的でこれらを引き続き使用するでしょう。 しかし、コンポーネントをより小さなコンポーネントに分割することを強制する自然な再レンダリング機能はもう存在しません。 コンポーネントが大きくなっても悪影響はありません。
どこでも useMemo/useCallback は不要になります
当然のことながら、すべての
useMemoそしてuseCallback私たちのコードを時々悩ませるものはなくなります。 その部分が私を最も興奮させます。 コンポーネントをメモ化するためだけに複数のレベルのコンポーネントを介してプロップを追跡する必要はもうありませんonSubmit小道具のコールバック。 読み取り不可能でデバッグ不可能な一連のコードはもう必要ありません。useMemoそしてuseCallbackすべては相互に依存しており、理解することは不可能です。 という理由だけで壊れたメモ化はもう必要ありませんchildrenメモ化されておらず、誰も気づきませんでした。相違点と調整
説明の仕方を変える必要があるかもしれない React の差分と調整。 現在の簡単な説明は、次のようにコンポーネントを「レンダリング」すると、
、その要素を作成するだけです。 この要素はその形状のオブジェクトです。{
"type": ...,
"props": ...,
}
ここで、「type」は文字列またはコンポーネントへの参照のいずれかです。
このコードでは:
const Parent = () => {
return <Child />;
};
いつ
Parent再レンダリングされ、その機能がトリガーされ、オブジェクトが再作成されます。 React は再レンダリングの前後でそのオブジェクトの浅い比較を実行します。その参照が変更された場合、それは React がそのサブツリーで完全な diff を実行する必要があることを示します。現時点では、これが原因です。
コンポーネントは、小道具がない場合でも、常に再レンダリングされます。 結果として(これは、React.createElement関数呼び出し) は常に再作成されるオブジェクトです。つまり、浅い比較チェックに合格することができません。React Compiler では、Elements、diffing、reconciliation の概念は同じままなので、それは良いことです。 しかし、どうやら
を返します メモ化されたオブジェクト そのプロパティが変更されていない場合。 したがって、実際には、コンパイラの最終結果は、すべてがラップされたものとほぼ同等になります。useMemo、エレメントさえも。const Parent = () => {
const child = useMemo(() => <Child />, []);
return child;
};
ただし、これは限られた公開リソースから推測しているだけなので、多少間違っている可能性があります。 いずれにせよ、これは単なる実装の詳細であり、製品コードにとってはあまり重要ではありません。
それ以外はほぼ現状と同じです。 他のコンポーネント内でコンポーネントを作成することは引き続き行われます 大規模なアンチパターン。 まだ使います 「キー」属性 要素を識別するか、状態をリセットします。 コンテクスト 対処するのはまだ難しいでしょう。 そして、データの取得やエラー処理に関することはすべて会話の一部ですらない。
とにかく、コンパイラのリリースが待ちきれません。 私たちの React 生活が大幅に改善されたように思えます。 たとえそのせいで記事の半分を書き直し、YouTubeビデオの半分をやり直す必要があったとしても 😅
#React #コンパイラと #React
