Deno DeployでReactコンポーネントをSSRする
asyncコンポーネント、SuspendコンポーネントもSSRできてしまったので、ReactDOMすげーなという気持ちです。
どうやる
結論から言うと、 renderToReadableStream を使えば、SSRできてしまうし、Suspendコンポーネントを使ったロード画面もできてしまうという、簡単さでした。
具体的なコード
まずは、一番外側の部分の実装はこうなります。 ただし、Appコンポーネントは定義済みとします。また、useStateなどの状態は持っておらず、あくまでSuspenseやasyncコンポーネントなどをもとに、静的なHTMLが生成されるだけのコンポーネントだとします。
驚くほど簡単ですよね。
import React, { Suspense, use } from "https://esm.sh/react";
import { renderToReadableStream } from "https://esm.sh/react-dom/server";
Deno.serve(async () => {
const html = await renderToReadableStream(<App />);
return new Response(html, { headers: { "content-type": "text/html" } });
});大事なのは、 renderToReadableStream で、こいつが内部でSuspenseやasyncコンポーネントをええ感じにしてくれているのでした。
asyncコンポーネントはなんとなくわかるが、Suspenseはどうなっているんだ
Suspenseは非同期のデータ取得が完了したタイミングでそのHTMLタグを書き換える処理が行われます。
つまり、ストリームで送られてくるので、そのようなことが実現できてしまうわけです。
- Suspenseのfallbackの部分を使ってコンポーネントを描画し、HTMLを生成しストリームとして返す
- ブラウザーはストリームから受け取ったところまでを描画する(この時点でHTMLがロード中の表示のまま正しく描画されています)
- Suspenseの待ち処理が終わったら、Suspenseの中のほんとのHTMLをストリームで返す(hidden属性を付けて非表示状態にしておく)
- こうすることで、ロード中のHTMLとSuspenseの中のHTMLが別々の場所に描画されている状態になる
- 追加でscriptをストリームで返す
- scriptでは、元のHTMLのSuspense fallbackの箇所を取り除き、hidden属性を付けてあったHTMLをfallbackの場所に差し込むようなJSが書かれている
- その結果、Suspenseの中身が正しく描画される!
おもろーって感じですね。
Deno Deployでなくても動きますが、無料でSSRできるのは強みだと思うので、おすすめです。
サンプルはこちらで動きをみることができます。通信内容を見てみるといいですね。