Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Server Component を理解する(したい)

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for morio morio
July 24, 2023
30

Server Component を理解する(したい)

Avatar for morio

morio

July 24, 2023
Tweet

Transcript

  1. それってなに? • サーバだけで動く React のコンポーネント • 2018 年に発表され、2020 年に最初の RFC

    が出た • https://github.com/reactjs/rfcs/blob/main/text/0188-server-componen ts.md • 2022年12月に出た Next.js 13 の App Router でパンピーも試せるようになった • 現時点で唯一のフレームワークに載った RSC の実装 • 先月に出た Next.js 13.4 で App Router は stable になりました
  2. とりあえず実装してみた • Next.js で、同じ内容のアプリを App Router と Page Directory で作ってみ

    る • Todo アプリ • DB あり • Todo の作成/削除 • Todo のトグル • Todo の検索
  3. これまで • DB へのアクセスは API Routes を立てて行う • マウント時、useEffect で

    API に問い合わせ • クライアントで state 作ってデータ入れる
  4. 登場人物紹介 • Server Component(SC) • サーバでしか実行されないコンポーネント • hooks(useState, useEffect)、イベントハンドラが使えない •

    Client Component(CC) ◦ サーバ、クライアントで実行されるコンポーネント ◦ これまでのコンポーネントと同じ ▪ hooks、イベントハンドラが使える • app directory のコンポーネントは基本 SC • “use client” で CC
  5. App Router • API は立てる必要なし、コンポーネントの中で直接 DB へのアクセスを記 述していい • Server

    Actions で更新系も表現可能 • useEffect は不要 • コンポーネントのトップレベルで await する • Suspense 使える • 問い合わせの結果を保持する state は不要 • クライアントに配信する JS のサイズも減る
  6. const Todo = () => { useEffect(()=> { fetchTodo().then((v) =>

    setTodo(v)); }, []) if(!todo) return <>Loading…</> return (<> … <Author /> </>) } const Author = () => { useEffect(()=> { fetchAuthor().then((v) => setAuthor(v)); }, []) if(!author) return <>Loading…</> return (<> … <Tag /> </>) const Tag = () => { useEffect(()=> { fetchTag().then((v) => setTag(v)); }, []) if(!tag) return <>Loading…</> return (<> … </>) }
  7. const Todo = () => { const fetchAll = async

    () => { await Promise.all([ fetchTodo(), fetchAuthor() fetchTag() ]).then(([todo, author,tag]) => { setTodo(todo) setPosts(posts) setTag(tag) }) } useEffect(()=> { fetchAll() }, []) if(!todo) return <>Loading…</> … }
  8. Relay Relay (Meta 製 GraphQL Client)は、Fetch-on-render のように colocation し つつ、

    Render-as-you-fetch へ移行する方法を提供する https://relay.dev/docs/guided-tour/rendering/queries/#render-as-you-fetch • Fragment でコンポーネント自身のコードとデータ取得のコードを並置 • 親で一括でデータ取得することで、Waterfall を避ける • Suspense でローディング境界を定義できる Relay & GraphQL じゃないと最高になれないのか?😡
  9. というわけで RSC は、GraphQL や Relay などのライブラリに依存せず、React のコアで Waterfall 問題を解決しようとするもの サーバ/DB間であれば

    Waterfall があったとしても程度を抑えられる そしたら他にもいいことがあった: • バンドルサイズ削減 • コードスプリットの自動化
  10. どうやっているのか - JSX ツリーをシリアライズしたものをサーバから送信 - HTML は送らない - クライアントでデシリアライズして、クライアント側のツリーとマージし て更新

    • https://github.com/reactwg/server-components/discussions/5(最初に php のコードが出てきたりしておもろい) • https://www.plasmic.app/blog/how-react-server-components-work
  11. 1⃣: サーバにページの JSX をシリアライズして送るようリクエスト JSX は React.createElement() の糖衣構文 { "type":

    "div", "key": null, "ref": null, "props": { "children": "hoge" }, "_store": {} } console.log(<div>hoge</div>) →
  12. 2⃣: サーバでデータを取得してシリアライズ シリアライズはサーバで行われるので、SC では Node.js の API を使える シリアライズ不能なものは SC

    の props に書けない(イベントハンドラ) → const Post = async ({postId}) => { const data = await db.findOne(...) return (<main> .... </main>) }; { “type”: “main”, “props”: { “children”: [ { … } } }
  13. const Fetcher = async () => { const data =

    await fetch(…) return … } const Parent = () => { <> parent <Suspense fallback="loading"> <Fetcher /> </Suspense> </> } J0:["$","",null,{"children”:[“parent”,[“$”,”$2",null,{"fallback”:”loading”,”children”:”…”}]]}] S2:"react.suspense" J0:["$","",null,{"children”:[“parent”,[“$”,”$2",null, …]]}]
  14. 4⃣: クライアントでデータを受け取り、JSXにデシリアライズする • その際CCの参照が含まれる場合は動的に import しつつ、復号された JSX に マージ •

    こうすることでCCのステートを保持しつつ、更新(mutation, navigation)が あった場合は新しいデータで画面を更新できる
  15. SSR と RSC はどう違うのか? - SSR と RSC は別物で、併用可能 App

    Router では、初回アクセスは HTML を SSR で返し、以降は RSC のフォーマットでやり取りする
  16. まとめ • RSC とは、React チームが勧める新しいデータ取得のための戦略 ◦ Server Component と Client

    Component がある ◦ データ取得をよりいい感じに書ける ◦ Waterfall の解決が元々のモチベーション ◦ SPA に、MPA の良いところを取り込む • プロダクション投入できる? ◦ Server Actions が stable になるまでは早いかも