Closed12

【key-front】React Server Component(RSC)をまとめる

1zushun1zushun

モチベーション

  • 毎週木曜日Slackのkey_frontチャンネルでハドル機能を使いお題に対してメンバー同士ディスカッションをする時間を15〜30分程度設けている
  • 今回は「React Server Component(RSC)」について取り上げる
  • ファシリテーターは筆者なので、事前に読み込んで気になった点などをスクラップに投げていく
  • 開催日は11/9(木)で最終的に議事録として結論をまとめる

過去に開催したNext.js関連の内容

https://zenn.dev/shuuuuuun/scraps/74aa82d164028f

https://zenn.dev/shuuuuuun/scraps/086df35458e77c

1zushun1zushun

ざっくりと今までのReact(Next.js)の歴史を振り返る

React = SPA(CSRのみ)

課題:初期表示の遅延(他にも色々あるが...)
解決策:事前レンダリング

※詳細は下記で触れている

https://zenn.dev/shuuuuuun/scraps/74aa82d164028f

Next.js = SPA(CSR + 事前レンダリング)

課題:ハイドレーション(他にも色々あるが...)
解決策:RSC

※詳細は下記で触れている(JavaScriptのロード時間を短くする手法について取り上げている)

https://zenn.dev/shuuuuuun/scraps/086df35458e77c

ここまで述べてきたが紹介したスクラップだと局所的かもしれないので、より体系的にReactの歴史に触れている下記を見た方が良いと思います!

https://zenn.dev/suzu_4/articles/2e6dbb25c12ee5

1zushun1zushun

https://vercel.com/blog/understanding-react-server-components

上記を読んで色々思ったことをメモしていく。

What did server-side rendering and React Suspense solve?

SSR focuses on initial page load, sending pre-rendered HTML to the client that must then be hydrated with downloaded JavaScript before it behaves as a typical React app. SSR also only happens one time: when directly navigating to a page.

特にSSR also only happens one time: when directly navigating to a page.をVercelから言質取れたのはかなり大きいと個人的に思った。

理由としてはNext.js公式にNext.js pre-renders every page.という箇所があり、同じページ内にSPA Navigationの挙動については触れられていないのもあるが、全てのページはSSRしたHTMLをFirst Load, SPA Navigation関係なしに生成・表示すると思っていたから。(以下参照)

https://nextjs.org/learn-pages-router/basics/data-fetching/pre-rendering

少し脱線するが、Danさんは従来のSSRでは、HTMLを生成するのは最初のページロードのときだけだった。と述べている。

さらにDanさんはRSCは最適化ではありません。これはパラダイムの一部であり、最初の読み込みだけでなく、ナビゲーションの際にも機能する。とRSCについても述べている。

So it’s really like two separate stages, and it’s important to know that traditional SSR, so generating HTML - it’s only ever useful for first page load. So when you load the page for the first time, you want to show something sooner, especially if it’s like content-oriented… So it’s really – it’s not a part of the paradigm; it’s an optimization. So server-side rendering is just the thing that lets you show something faster, before all the JavaScript loads, and the page becomes alive. But RSC is not that. RSC is not an optimization. It’s the part of the paradigm, and it works not just for the first load, but for navigations. So if my page has already loaded, and I’m seeing for example like the feed, and then I click Profile, then that goes directly through the RSC, and then this is where my profile component can read the profile from the database, decide what to display, and then that gets streamed to the client, and I have an SPA-style transition where it doesn’t reload the page, it just kind of updates organically within it.

https://changelog.com/jsparty/267#t=1342

What do React Server Components do?

In order to solve the above issues, React has created Server Components. RSCs individually fetch data and render entirely on the server, and the resulting HTML is streamed into the client-side React component tree, interleaving with other Server and Client Components as necessary.

This process eliminates the need for client-side re-rendering, thereby improving performance. For any Client Components, hydration can happen concurrently with RSCs streaming in, since the compute load is shared between client and server.

This process eliminates the need for client-side re-rendering(クライアント側の再レンダリングが不要になる)というのが腹落ちしなかった。

主語がThis Processで始まっているので、プロセス全体を指しているならClient Componentsも含まれた意味になるから再レンダリング(ハイドレーション)は必要になると思うのだが何故...?RSC server 〜 ClientではなくRSC server 〜 SSR serverまでの話?

前の文章の主語がServer Componentsなので仮にThis ProcessのことがServer Componentsを指しているのであれば納得できる気がする。

ここら辺刺されそうな気がするので要調査。

RSCs: Interleaving and Suspense integration

It's important to note that Client Components are still SSR'ed on initial load. The RSC model does not replace SSR or Suspense, but rather it works alongside them to provide all parts of your application to your users as they need them.

こちらの内容については下記のツイートと同じ。①RSCとプレレンダリング(SSR / SSG)は役割が異なる②RSCとRSCとプレレンダリング(SSR / SSG)は一緒に使われる。

https://twitter.com/dan_abramov/status/1650147864243232769

RSCはSSR/SSGとは独立している。SSR/SSGなしでRSCを持つこともできるし、RSCなしでSSR/SSGを持つこともできる。しかし、これらは一緒に機能するように設計されているので、本番用のRSCセットアップはRSC+SSRかRSC+SSGのどちらかになるはずだ(あるいは、Next App Routerのように両方あればなお良い)。

下記でも同様のことについて触れているのでメモ

https://twitter.com/potetotes/status/1341141198258524163

Improved data fetching with Next.js

Perhaps most importantly, fetching data on the server helps to prevent client-side data fetching waterfalls, where requests stack up against each other and have to be resolved in serial before the user can continue. Server-side fetches have a much smaller overhead, as they don't block the whole client and they resolve much more quickly.

ウォーターフォール問題はSuspenseでも改善に取り組んでいるはず。RSCでも解決できるという解釈をした。

参考記事

https://zenn.dev/ikenohi/scraps/2fea6d9e260cb4#comment-0d3b57fef18f6d

https://jp.quora.com/Reactのserver-componentって何でしょうか

1zushun1zushun

RSCと多段階計算

かなり腹落ちした。次に紹介するディスカッションでの画像でも説明されているように今のNext.jsのフローにもう一階層できたイメージ。

https://zenn.dev/uhyo/articles/react-server-components-multi-stage


多段階計算の記事でも触れられていたが、SSRはReact Clientとのこと。

We will say React Client (or just capitalized Client) to mean any environment that consumes the React Server output. As you've just seen, SSR is a React Client — and so is the browser. We don't support components on the Client yet — we'll build that next! — but it shouldn't be a huge spoiler to say that we will call them Client Components.

※下記から画像を参照

https://github.com/reactwg/server-components/discussions/5

https://zenn.dev/dai_shi/articles/94affd526f4c8a


こちらのディスカッションで触れられている内容(画像)もわかりやすい。

One way to think about it is that in RSC, "Server" and "Client" doesn't directly correspond to a physical server and client. You can think of them more as "React Server" and "React Client". Props always flow from React Server to React Client, and there is a serialization boundary between them. React Server typically runs either at the build time (default), or on an actual server. React Client typically runs in both environments (in the browser it manages the DOM, in other environments it generates initial HTML).

Props always flow from React Server to React Client, and there is a serialization boundary between them.については次で触れる。

※下記から画像を参照

https://github.com/reactwg/server-components/discussions/4

1zushun1zushun

RSCのレンダリングフローに触れる前に、一旦SSRってなんだっけということでSSRのレンダリングフローをまとめる

SSR のレンダリングフロー

味噌はrender components to HTML(HTML)とbundle components as code(JavaScript)

※下記から画像を参照

https://github.com/reactwg/server-components/discussions/4

https://qiita.com/newbee1939/items/7ce919f9a1a7153582b8


HTMLと一緒にinlined tree for hydration とのこと

※下記から画像を参照

https://github.com/reactwg/server-components/discussions/5


HTMLだけでなくJavaScriptも付与されているのを確認することができる

※下記リンクからサンプル検証可能

https://magicode.io/Sumiren/articles/9a182ba35cc0410fbe6917d84eff2488


最終的には先の情報と下記を照らし合わせて納得した

このHTMLには、コンパイルされたReactアプリ及びランタイム(bundle.js)がJavaScriptとして付与されていて遅延ローディング設定されている。

HTMLだけでなくJavaScriptも付与されている

※下記から画像を参照

https://qiita.com/uehaj/items/1b7f0a86596353587466#一般的なssrnextjsなどの動作

https://jp.quora.com/SSRで返すhtmlと従来のphpなどで返すhtmlファイルの違い-または/answers/321311204

1zushun1zushun

Server Components

公式ドキュメント

https://nextjs.org/docs/app/building-your-application/rendering/server-components

日本語で触れている記事

https://zenn.dev/frontendflat/articles/nextjs-rscpayload-composition

Client Components

公式ドキュメント

https://nextjs.org/docs/app/building-your-application/rendering/client-components

日本語で触れている記事(※レンダリングフローのシーケンス図あり)

https://zenn.dev/n0aaa_eth/articles/b9baf713dab252

1zushun1zushun

RSC のレンダリングフロー

いろんな記事を見ていると下記が取り上げられていることが多い。だいたい他の記事に関しても下記のフローを紹介しているので目を通す。

https://postd.cc/how-react-server-components-work/

https://www.plasmic.app/blog/how-react-server-components-work

  1. サーバーは、サーバーコンポーネントを通常通り「レンダリング」し、Reactコンポーネントをdivや pといったネイティブなhtml要素に変換する。
  2. ブラウザでレンダリングされる予定の「クライアント」コンポーネントに遭遇すると、代わりにプレースホルダーを出力し、この穴(プレースホルダー)を正しいクライアントコンポーネントとプロップで埋めるよう指示する。
  3. そして、ブラウザがその出力を受け取り、クライアントコンポーネントでその穴(プレースホルダー)を埋める。

https://zenn.dev/msy/scraps/361bc12c90e204

https://zenn.dev/msy/articles/a042024e12fca1

1zushun1zushun

歯抜けのSSR

個人的な解釈としてもSRC = 「歯抜けのSSR」はイメージとして腹落ちしやすかった

SSR

  • サーバサイドでページ全体を完全にHTML化する

RSC

  • クライアントコンポーネントを除いた部分のみをサーバサイドでHTML化します。(歯抜けのサーバサイドレンダリング)
    • SSRと比べると、サーバサイドでページ全体を完全にHTML化するのではなく、クライアントレンダリングする部分を「残す」
    • RSCにおけるサーバコンポーネント部分は、SSRとは異なりクライアント上では絶対実行されることはない

https://jp.quora.com/ReactにおけるSSR-Server-Side-Rendering-とReact-Serverコンポーネントの違いは何です

1zushun1zushun

まとめ

今回のスクラップで以下について調査した

  • React(Next.js)の課題
  • Understanding React Server Components(記事)
  • RSCと多段階計算
  • SSRのレンダリングフロー
  • SCとCC
  • RSCのレンダリングフロー
  • SRCと歯抜けのSSR
  • RSCを導入するメリット

特にUnderstanding React Server Components でもあったようにSSRとRSCは別物であり、組み合わせることでよりパフォーマンスの向上が図れることがわかった。(SSRでLCPの改善、RSCでINPなどの応答性の向上)

多段階計算と歯抜けのSSRのくだりでRSCがどのようなフローを辿っているのかイメージをだいぶ掴むことができた。より厳密に理解するためには次の記事が適していると思った。

https://postd.cc/how-react-server-components-work/

RSCをまとめるついでにSSRのレンダリングフローへ寄り道をしSSRの理解もより深めることができた。今度はハイドレーション周りも合わせて説明する用意をしておきたい。

あとはkey-front当日に発表して本スクラップに議事録を投げる。

1zushun1zushun

議事録_20231109

  • 11/9(木)に実施
  • 結論を出すコンテンツではなかったため特に結論はなし
  • 参加人数は10名(以下エビデンス)

RSCをどうやったら使えるか?

  • Next.js App Routerを想定

appディレクトリ以下はデフォルトでSCになるCCにする場合は"use client"を宣言する必要がある

Next.js App Routerでディレクトリ構成はどうするか?

  • 具体的にはcomponentsはどこに配置するか?

こちらも検討中。private folderで密結合させる記事や、appディレクトリと同じ階層にcomponentsをおく例も見るので悩んでいる

https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders

テストはどうするか?

調査不足のため不明、今の所はPresentaion / Containerパターンがキーになると思っている

https://quramy.medium.com/react-server-component-のテストと-container-presentation-separation-7da455d66576

このスクラップは6ヶ月前にクローズされました