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が先にクライアントに返却されるようになります。代わりにfallbackpropsに渡したコンポーネントが描画されます。
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>
  )
}
うれしい!
    
Top comments (0)