コンポーネント表示までの一時表示を指定できるSuspense
Suspenseは、コンポーネントの表示までに時間がかかる時の「ロード中」状態を指定できる機能です。親コンポーネントの表示後、5秒経過後に子コンポーネントが表示される図11のサンプルで説明します。
Suspenseを指定するには、リスト10の通り記述します。(1)の部分がSuspenseで、fallback属性にロード中の表示を指定した<Suspense>要素で、Subコンポーネント(<Sub />)を囲みます。
return ( <div style={{backgroundColor: 'lightpink'}}> <h3>React 18 Suspense</h3> <div>ここは親コンポーネントです</div> {/* ここにSuspenseを指定してSubコンポーネントを配置 ...(1)*/} <Suspense fallback={<div>ロード中です...</div>}> <Sub /> </Suspense> </div> );
Subコンポーネントの記述はリスト11の通りです。
let isLoaded = false; // ロード中を表す変数 ...(1) function Sub() { // ロード済みの場合→コンポーネントの表示内容をJSXで記述して返却 ...(2) if (isLoaded) { isLoaded = false; // 次の表示に備えてフラグを戻す return ( <div style={{backgroundColor: 'lightgreen'}}> <h4>子コンポーネント</h4> <div>ロード完了!</div> </div> ); } // ロード中の場合→5秒後に終了するPromiseをthrow ...(3) else { throw new Promise(resolve => { setTimeout(() => { isLoaded = true; resolve(); }, 5000) }); } }
ロード中を表す変数isLoadedをfalseで初期化します(1)。コンポーネントの記述では、ロード済みの場合(2)はコンポーネントの表示内容を返却し、ロード中の場合(3)は、5秒後に成功するPromiseをthrowするようにします。
ReactのSSR機能とSuspense
React 18ではSSR機能を利用時に、Suspenseに対応してHTMLをストリーム出力するrenderToPipeableStream(Node.js用)、renderToReadableStream(DenoなどのWeb Stream用)が利用できるようになりました。本記事ではrenderToPipeableStreamの例を紹介します。
//HTML要素をJSXで記述 ...(1) const htmlElem =(略); // HTMLをストリームするように記述 ...(2) const { pipe } = ReactDOMServer.renderToPipeableStream( htmlElem, { // ハイドレーションを行うJavaScriptファイル ...(3) bootstrapScripts: ['/index.js'], // ストリームの処理 ...(4) onShellReady() { res.setHeader('content-type', 'text/html'); pipe(res); } } );
(1)で記述したHTML要素を、(2)のrenderToPipeableStream関数の第1要素に指定します。第2引数のJavaScriptオブジェクトには、(3)でハイドレーションを行うJavaScriptファイルを指定し、(4)にはWebブラウザーにコンテンツをストリームする処理を記述します。この処理により、Suspenseを含むWebページは、まずSuspense中の内容がHTMLとしてダウンロードされますが、HTTPの接続はここで切断されず、Suspense終了後に、新しい表示内容と、表示を更新するJavaScriptが追加でダウンロードされます。
まとめ
本記事では、React 18の新機能について説明しました。前半となる今回は、新機能のベースになる並行レンダリングと、バッチング、TransitionやDeferredValue、Suspense、およびサーバーサイドレンダリングについて説明しました。
次回は、潜在的な問題点を洗い出すStrictモードへの機能追加や、新たなフックについて紹介します。