開発時にstrictモードでは2回レンダリングが行われる。
React18のstrictモードの仕様になります。yarn startなどの開発モードでアプリケーションを起動した場合だけ適用されます。yarn buildでビルド時には特に適用はされません。
この機能がある理由
開発時に事前に「マウント→アンマウント→マウント」という挙動を繰り返すことにより事前に予期しない副作用を抽出することを目的として実装されています。
このモードを解除するには?
index.js内の「<React.StrictMode>」というタグ内容を削除すれば開発時でも本挙動は解除されます。
1 2 3 |
root.render( <React.StrictMode> </React.StrictMode> |
Suspenseコンポーネント
データの受け取り状態を検知できるコンポーネント。
1 2 3 |
<Suspense fallback={<待機中に表示させたいコンポーネント />}> <任意のコンポーネント /> </Suspense> |
任意のコンポーネント(非同期処理があるもの)が返ってくるまで待機中に表示させたいコンポーネントをfallbackで指定できます。
仕組みとしてはPromiseオブジェクトの状態を監視してpendingになっている場合に指定したコンポーネントを表示してくれます。(Promiseがfulfilledや、rejectedになったら終了です。)
また、Suspense自体はネストもできるので「あるコンポーネントはこのデータロードが終わるまで出す」、「さらに別コンポーネントは別途待つ」などの記述もできたりします。
メリット
「ローディングコンポーネント」と「任意のコンポーネント」のロジックを分離できるので良い。
Suspense on Server
非同期で処理を待っている間は部分的にレンダリングできる。SSRが重くならない。
今だったらISR(アクセスがあるごとにインターバルを置いて新しいサイトを生成する技術)でレンダリングする戦略はある。
Streaming Server Rendering with Suspense
準備されたコンポーネントからブラウザに描画していく。描画の遅いコンポーネントがあれば他のコンポーネントの描画もそれに引きづられて遅くなってしまう。
各コンポーネントを並列で描画できるようになる。
今までは全てのコンポーネントが描画されるまではクリックなどのイベントハンドラも実行することができなかったが、あるコンポーネントの描画が完了するまで待たなくても別コンポーネントのクリックなどをできるようになった。
Nested suspense
Suspenseは入れ子にできます。入れ子にした場合は外側のsuspenseから動き内側のsuspenseが動きます。
1 2 3 4 5 6 |
<Suspense fallback={<Spinner />}> <FetchUsers /> <Suspense fallback={<Spinner />}> <FetchTasks /> </Suspense> </Suspense> |
React Server Component
サーバー内でしか実行されない。
SPAとSSRを共存できる機能。SPAで表示させたいものをclient.jsで指定し、SSRで表示させたいものはserver.jsというファイルを作ってserver.js側でclient.jsをインポートして使う。
メリット
- クライアント側にライブラリなどがバンドルさせなくても良くなる。(バンドルサイズは全てにおいて悪だしできるだけ小さくした方が良い)
- データソースにダイレクトにアクセスできる。(API経由で呼び出さなくてもよくなる。)
制約
- server.js内でしかこの機能は使えない。
- useStateは使えない。
ステート更新
automatic batching
従来だと、stateを更新するたびに再レンダリングが行われることになる。それがstateが何度更新されようとレンダリングは1度だけ行うようにするなどをしてくれる機能。正確に言えば、前からauto batchingの機能自体はあったのですが、18からはpromiseなどにも対応されました。(17までは対応していなかった。)
1 2 3 4 5 |
setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) }, 1000); |
複数stateが更新される場合は、最後のstateが更新された場合のみ再レンダリングを実行するというような仕様に変更になっています。
ユースケース
axiosなどでAPIを実行して取得した処理結果などでstateを複数更新する場合でも一度しか再レンダリングが発生しなくなります。
この挙動を無効化したい場合(React17と同じ挙動にしたい場合)
flushSyncを使います。以下のようにsetState内をそれぞれflushSyncの構文で囲います。React17と同じようにそれぞれでレンダリングされるようになります。
1 2 3 4 5 6 7 8 9 10 |
import { flushSync } from 'react-dom' axios.get('URL').then((res) => { flushSync(() => { setA(res.a) }) flushSync(() => { setB(res.b) }) |
コンカレントモードを実現するための新APIが追加される。
useTransition、startTransition
基本は使わないこと。他の代替手法でパフォーマンスが最適化できるか試してそれでもだめなら使うことを検討する。
React17まではstateの更新に優先順位をつけることはできなかった。優先度が高い処理を低い処理に割り込ませて実行させるというコンカレント(並列計算)な実行がReact18で可能になりました。
startTransitionで指定するstateは緊急性がないstateとみなすことができ特定処理のパフォーマンスを上げることができる。
例えば、「フォーム画面でテキストボックスへの入力および反映は緊急性が高いので優先度を上げたい」みたいな場合に使えます。
例
フォームに入力した値を元にフィルターする処理があるとしたら、キー入力の状態更新の優先度を上げて、フィルターは少し優先度を下げるなど。
実装例
1 |
const [isPending, startTransition] = useTransition() |
isPending
trueなら優先度が高いstateが更新されていて、優先度が低いstateがまだ更新されてないことを指します。falseなら全てのstateが更新されている状態を指します。
例えば、以下のようにinputとupdateValueという状態があったとしたらstartTrasitionを指定している状態(updateValue)は更新を遅延させることが可能です。
1 2 3 4 |
const update = (e) => { setInput(e.target.value) startTransition(() => setUpdateValue(e.target.value)) } |
もしそこまでパフォーマンスが悪くないページの場合に効果を体感したい場合はChromeの開発者ツールのPerformanceタブの中のCPUの値を「4x slowdown」などに変更することで検証することが可能です。
startTransitionを付けていないstateの更新は仮に上記のようにスローダウンしていたとしても速攻で描画が終わります。
useDeferredValue
基本的にはuseTransitionと同じように使うのでuseTransitionを優先して使えばよいです。
ただ、一点だけ用途が異なる。
useTransitionだと更新用関数をstartTransitionで囲うことになる。しかしライブラリなどを利用している場合などで使えないケースもあります。
1 2 3 |
const 遅延させたい値 = 関数(); const 遅延後の値 = useDeferredValue(遅延させたい値) |
useTransitionは遅延させたい関数をラップしていたが、useDeferredValueは遅延させたい値をラップします。
ちなみに上記例でいえば、「遅延させたい値」と「遅延後の値」は中身は同じなのですが、更新の優先度が他の値に比べて下がります。
この記事へのコメントはありません。