Haruto Hirakawa
Posted on November 2, 2022
React16.6から追加され、いまだにexperimentalではある<Suspense />
というコンポーネントがあります。React18から Streaming SSR が実装され、コンポーネントを非同期にレンダリングできるようになりました。それによってSSRを効かせたアプリケーションを部分的に読み込ませたままユーザーに返すことで実時間を短縮してユーザー体験を向上させよう!という話です。
SSRについて
そもそもSSRのデータフローについて軽く話しておきます。ここでは、レンダリングの際に何らかのWebAPIからデータをFetchするアプリケーションを想定します。
まずクライアントがURLにアクセスしてリクエストを飛ばします。リクエストを受け取ったサーバーはgetServerSidePropsを通してWebAPIからデータをFetchしてきます。データのFetchが完了するのを待った上でpropsが定義され、コンポーネントがレンダリングされます。
export async function getServerSideProps(context) {
const data = await fetch('...')
return {
props: {
// ...
},
}
}
レンダリングされたHTMLをクライアントに送り、JSを読み込み、最終的にHTMLにHydrateします。Hydrateが完了するとユーザーがWebページを操作できる状態になります。
さて、この段階で「待たなければいけない瞬間」が存在しています。データのFetch、JSの読み込み、Hydrate、これらの処理が実行されている間ユーザーは待つしかありません。フロント側でいくらチューニングされていても、呼び出したWebAPIが遅ければ、それだけでページのレスポンスが悪くなってしまいます。
Streaming SSR
目玉機能の登場です。こいつの登場によって、これらの「待たなければいけない瞬間」を有効活用できるようになります。
例えば、以下のようなコンポーネントが存在するとします。非同期にデータをfetchするコンポーネントCがある場合、fetchが完了するまで関係のないコンポーネント(A、B)も待つ必要がありました。しかしReact18からは以下のように<Suspense />
コンポーネントでラップしたコンポーネントを除いたコンポーネントのHTMLが先にクライアントに返却されるようになります。代わりにfallback
propsに渡したコンポーネントが描画されます。
import { Suspense } from 'react'
const Component = (props: Props) => {
return (
<Container>
<A />
<B />
<Suspense fallback={<Loading />}>
<C />
</Suspense>
</Container>
)
}
クライアントサイド側でコンポーネントCのデータFetchを行い、完了した時点でレンダリングされます。これによってAPIによるページ全体のレンダリングの遅延は解決できるようになりました。
Hydrate
しかしこの状態、表示はされますがページが操作可能な状態にはなっていないので、周りのUIはハリボテ状態になっています。しかし、React.lazy
を組み合わせることで、Fetchが完了する前にコンポーネントCを待たずにHydrateさせることができます。(<Suspense />
と組み合わせることでSSRでも効くように)
import { Suspense, lazy } from 'react'
const C = lazy(() => import('./C.tsx'))
const Component = (props: Props) => {
return (
<Container>
<A />
<B />
<Suspense fallback={<Loading />}>
<C />
</Suspense>
</Container>
)
}
うれしい!
Posted on November 2, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.