ちゃなべの備忘録

ほぼ備忘録です。

RSCとかがわからなかったから完全に理解するまで【備忘録】

はじめに

panda CSSを触ってみたんですが、サーバー側でbuildするので、クライアント側での変更をいちいちCSSに反映するのが難しそうに思える。

まぁぼくの調査不足だと思うので、一旦pandaCSSを調査することにした。
その時に出てきたのが、「RSC互換性」というワードであり、いわゆるReact Server Components との互換性のことである。

「いや、、RSCよく知らんな」ということで調査します。

調査

まっさきにこの記事が出てきたので読んでまとめる。

zenn.dev

  • RSCはReactコンポーネントをサーバーサイドでレンダリングする技術
  • もちろんClientでのレンダリングも両立が可能なので、部分的に処理を早くしたりなどができる
  • また、React Server ComponentsはConcurrent Modeに完全対応し、それを前提として動作します。
  • RSCはコンポーネントを三種類に分類します
  • Server Components
    • サーバーのみでレンダリングされるコンポーネント
    • 拡張子を.server.jsとするとServer Componentsとして解釈されます
    • 以下の制約を持ちます
      • ステートを持てないため、useStateuseReducerは使えない
      • リレンダーが走らないので、useEffectuseLayoutEffectは使えない
      • クライアントとは分離された環境でレンダーされるため、useContextは使えない
      • ブラウザのみ利用可能なAPI (DOMやWebAPI)は使えない
    • Server ComponentsはClient Componentsを子コンポーネントに持つことができます
  • Client Components
    • 従来のクライアントのみでレンダリングされるコンポーネントのこと
    • 従来のコンポーネントですが、拡張子を.client.jsとする必要がある
    • ステートを持ったり、イベントをハンドルしたり、WebAPIを利用したりする必要があるときに利用
    • Server Componentsをimportすることはできません
      • Server componentsのレンダーに必要なサーバーへのリクエストのせいでのパフォーマンス低下を防ぐため
    • childrenなどのpropを通じてServer ComponentsをClient Componentsに渡すことはできる
      • これはServer ComponentsをレンダリングしたJSXをpropsに渡すだけなので、ただのJSXを渡すことに等しい
  • Shared Components
    • サーバーとクライアントのどちらでもレンダリング可能なコンポーネントのこと
    • Server Componentsから呼ばれた場合はServer Componentsとして、Client Componentsから呼ばれた場合はClient Componentsとして振舞います
    • 拡張子は.js
    • 両方の制約を持ちます。ステートは持てずで、Server Componentsをimportすることもできません
  • 筆者が考えるベストプラクティスは以下
    • 基本的にパフォーマンス有利なServer Componentsにする
    • ステートが必要だったり、イベントをハンドルしたかったりするときはClient Components
    • 両方で使いたいときはShared Componentsに。
  • RSCの恩恵はパフォーマンス面が主
    • バンドルサイズの減少
      • Server Componentsのコードはクライアントがダウンロードするバンドルに含まれない
      • そのため、ユーザーのインタラクションに反応しない大部分のコンポーネントを、バンドルから取り除くことができる
      • これによって
        • 通信にかかるコストの減少
        • コードのパースにかかるコストを減少
        • Virtual DOMにマウントされるコンポーネントが減少 → メモリの使用量が減少
    • データフェッチにかかる時間の減少
      • Server Componentsはサーバー上で実行されるため、データに直接アクセスできることがあり、レイテンシーを大きく抑えられる
      • 一つのデータフェッチが完了した後に別のフェッチが開始するようなWaterfallの影響が小さくなります
    • Code Splittingの自動化
      • Server Componentsのレンダリングが終了した時点で、必要となるClient Componentsがどれかというのが判明します
      • RSCでは、自動でClient Componentsを切り分けてバンドル化し、必要となるもののみをクライアントに指定してダウンロードさせる
    • レンダー結果のDOMへの更新は最小限
      • Server Componentsが返すレンダリング結果はHTMLではなくReactコンポーネントです
      • そのため、自然にVirtual DOMにマージすることが可能
      • 変更されたDOM要素のみが更新され、マウント済みのClient Componentsの状態が失われることも、更新されなかったDOMの状態も失われない
  • SSRとは別技術であり、共存が可能です

Concurrent Modeが何かわからん、調べる。

www.ey-office.com

  • Concurrent Modeは日本語でいうと「並列モード」
  • 背景
    • JSはシングルスレッドですが、イベント駆動モデルです。イベントがあれば、進んでいた処理(画面表示など)を終わるのを待たずにそちらの処理に切り替えることができます。これによって並列であるかのように感じます
    • しかし大量の処理があるとクリックなどのイベントが反応できなくなり、UXが悪くなります
    • 仮想DOMは特に重く、React15.x まではレンダリングが終わるまでイベント等は反応しませんでした(ブロッキングレンダリング)
    • React16.0はレンダリングエンジンがReact Fiberに変わり、中断や切り替え可能な細かい単位でレンダリングを行えるようになりました
  • TransitionやDefferredValueが正式な並列ができるAPIとしてリリースされました

zenn.dev

  • Concurrent Reactは機能ではなく、新しいメカニズム
  • Suspense
    • 内部のコンポーネントがロード中でまだレンダリングできない状態を処理できる
    • 表示されるまで「ぐるぐる(Spinner)」を表示するとかね
    • だが、現状のサポートはReact.lazyくらい
    • useQuery などで、データを読み込む時にSuspenseを用いれば簡略化して記述可能。だけどまだ非推奨。