前回のNext.js12のReact18対応のNew Streaming SSRの続き
次は、React Server Componentsに関して、もう少し詳しいお話を聞く機会があったのでメモ
React Server Components
React Flightとも呼ばれ、RSCと略されるみたい
サーバーサイドで実行されるコンポーネントで、バンドルサイズの削除、サーバーサイドリソースの活用を目的として使用する
RSCの3種類のコンポーネント
お話しを聞いたときにでていた表がめちゃめちゃわかりやすかったので、そのまま抜粋させてもらう
| Server Components | Client Components | Shared Components | |
|---|---|---|---|
| 拡張子 | .server.js | .client.js | その他 |
| 実行場所 | サーバのみ | クライアントのみ | クライアントとサーバ |
| 状態(useState、useReducerなど) | x | o | x |
| 作用(useEffect、useLayoutEffectなど) | x | o | x |
| DOMアクセス(実DOM, onClick、onChangeなど) | x | o | x |
| サーバ側リソースの利用 | o | x | x |
| Server Componentsの利用 | o | x | x |
| Client Componentsの利用 | o | o | o |
| Shared Componentsの利用 | o | o | o |
Server Componentsを使う場合、従来のクライアントサイドでデータフェッチするようなコンポーネントが、サーバーサイドでレンダリングされるようになるのでデータフェッチもサーバーサイド行われる
これによりバックエンドが近くなることや、Server ComponentsそのものがAPI Gatewayになるといったメリットがある
RSCのレンダリング
コンポーネント単位でサーバーにアクセスすると思っていたが、そうではなくコンポーネントツリー単位でアクセスするとのこと
つまり、起点はServer Componentsだが、その子コンポーネント(Client ComponentsやShared Componentsも利用できる)も含まれている
HTTPレスポンスは独自のFlightプロトコルというものが使われ、以下のような改行区切りの「タグ:JSON」がフォーマットになる
M1: {"id":"./components/page.client.js","name":"default","chunks":[]}
S2: {"react.suspense"}
M4: {"id":"./components/footer.client.js","name":"default","chunks":[]}
J0: ["$","@1",null,{"children":[["$","$2",null,{"fallback":["$","div",null,{"className":"spinner"}],"children":"@3"}],...]}]
M23: {"id":"./components/story.client.js","name":"default","chunks":[]}
コンポーネントツリーが Server Components > Client Components となっている場合、以下のようにその下に Server Components を呼び出すことはできない
Order.server.js
import OrderView from "./OrderView.client" const Order = ({orderId}) => { const data = useOrderData(orderId) return ( <OrderView orderId={orderId} data={data} /> ) }
OrderView.client.js
import OrderItems from "./OrderItems.server" // ←これはNG const OrderView = ({orderId, data}) => (<> <div> {data} </div> <OrderItems orderId={orderId} </>)
なので、以下のようにServer ComponentsがClient Componentsの子であるServer Componentsをレンダリングする
Order.server.js
import OrderView from "./OrderView.client" import OrderItems from "./OrderItems.server" const Order = ({orderId}) => { const data = useOrderData(orderId) return ( <OrderView data={data}> <OrderItems orrderId={orderId} /> </OrderView> ) }
OrderView.client.js
const OrderView = ({data, children}) => (<> <div> {data} </div> {children} </>)
このような作りにこれからなるみたい
大枠の概念は理解することができた