memo @240217*
React Labs
はReact
の研究を行ってるところ。
• useMemo, useCallback, memo → React Compiler
• forwardRef → ref is a prop
• React.lazy → RSC, promise-as-child
• useContext → use(Context)
• throw promise → use(promise)
• <Context.Provider> → <Context>
useMemo
useCallback
memo
はメモ化(キャッシュのようなもの。一度行った計算をもう一度再計算させないようにする)するためのAPI
。useMemo
は変数useCallback
は関数を再利用する。memo
はコンポーネント自体をメモ化する。それら3つはReact Compiler
に置き換わる。React Compiler
はReact
を最適化するコンパイラ。今までReact
は再レンダリングが多いというネックがあったけどもそもそもReact
はデフォルトでとても高速だから無駄な再レンダリングが行われても影響しないほど。けどそこが他のライブラリからするとReact
よりも優れてるポイントであってそれを抑えるための方法としてそれらのAPIはあったけどそれらを適切に使用するのとても難しかった。ただReact Compiler
によってその懸念はもう無くなる。我々の書いてるReact
のコードはもともとコンパイルされてるし、そのお陰でブラウザでもサーバー側でも動くけども公式から提供されるこのReact Compiler
によってそれを通すことによってuseMemo
useCallback
memo
を使わなくとも自動的に最適化してくれる。
コンパイラはプログラミングにおいて非常に重要であり基本的にコンパイラを通すことによって最適化の恩恵も受けられるしバグも事前に減らせる。JSとTSでもJSはブラウザでそのまま動かせるけどもTSはコンパイルしてJSにして動かしてるし玄人が好んで使ってるのはやはりコンパイル型の言語。またReact Compiler
を通したら我々が書いてるReact
のコードのバッドなところを指摘してくれる。今もReact
のStrictMode
によって指摘してくれるけどコンパイラレベルとなるとよりそれが厳格になる。
forwardRef
はめっちゃ難しいしライブラリの開発者からすると必須のAPIであってもアプリの開発者からするとなくても開発できちゃう。内容としては自分が作ったコンポーネントやサードパーティーのコンポーネントを使うときにコンポーネント内部のDOM要素にref
を貼りたいようなとき使われる。それがforwardRef
はいらなくなってref
はただのprops
として扱えるようになる。
React.lazy
はレイジーロードするためのもの。レイジーロードとはパフォーマンスを上げるためには重要であって全部の画像を一気に読み込もうとするととても時間かかるからページを最初に訪れたとき映らない画像はあとから読み込ませるテクニック(ヒーロー画像はレイジーローディングさせないけどその下の画像はレイジーローディングさせるみたいな)
イメージ戦略は3つあってまず一番最初に表示するものは超高速で表示する。次のイメージはブラーで表示する(Next.jsには組み込まれてる機能)。3つ目は特にスマホだとかなりスクロールしないと表示されないしレイジーローディングする。
ただ実際にはReact.lazy
はどっちかというとif文の条件分岐とか使って、あとからコンポーネントを読み込ませたいようなときに使うことが多い。具体的にはユーザーがログインしてたらログイン用のページを出し、ログインしてなかったらログインしてない用のページを出すけども、そのときどっちのコンポーネントもインポートしてたらよろしくない。ユーザーがページに来てるときはログインしてる or してないの2択であるにも関わらず、どっちも読み込んでたら一つのコンポーネントが無駄になる。なのでそういう条件分岐のときはどっちのコンポーネントもReact.lazy
を使ってインポートするかNext.js
ならダイナミックインポートを使うかしてそのコンポーネントをあと読みさせる必要がある。これはバンドルサイズにものすごく影響する。React.lazy
するだけでバンドルサイズが全く変わる。
ただ今回そのReact.lazy
はRSC
に置いては不要になる。コンポーネントがRSC
のランタイムの段階で既に決定されるためReact.lazy
をしなくても自動的に最適化されるしそれはRSC
のいいところ。React.lazy
もNext.js
のApp Router
を使用する上ではいらないよねという話。
useContext → use(Context)
use
という新しいhooks
が出てuse
はContext
を読み込むこともできるし
if文などの条件分岐にも使用できるためuseContext
の上位互換となる。
throw promise → use(promise)
外部のデータフェッチするときもこれからはuse
を使用する。
<Context.Provider> → <Context>
今まではContext
にもProvider
とConsumer
という2つの考え方があったけどもコンポーネントもProvider
とConsumer
とあったけどもそれもConsumer
というContext
を読み取る方がuse(Context)
となるしそしたらもうContext
の中にProvider
しかないよねという話。