コンポーネント表示までの一時表示を指定できる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モードへの機能追加や、新たなフックについて紹介します。
