LoginSignup
2
2

React Server Componentsを活用したNext.jsの新構造

Last updated at Posted at 2024-03-15

これは何

Next.js App Routerでは、データfetchをとっとと下層に動かすと幸せという話。
幸せを噛みしめるために、今までfetchで困ってきた歴史?の説明。

クライアントサイドfetchの問題

ほとんどのデータは全ユーザーに共通のものだ。
これをクライアントサイドから

const Component = () => {
  const [data, setData] = useState();
  useEffect(() => {
    fetch('https://api.com')
      .then((res) => res.json())
      .then((json) => {
        setData(json);
      });
  }, []);
  return <>{data}</>;
};

このようにfetchすれば、サーバーサイドレンダリングのメリットの多くを失うことになる。

  1. 表示が遅い。ファーストビューにあればチラつきが見える。
  2. APIリクエストが多い。10分間キャッシュして良い内容であっても、10分で100人が来れば100回リクエストが出る。
  3. ややこしい :expressionless:

Pages Routerでの問題

Pages Routerでは、サーバー上でのみfetchする独自の方法が用意された。
getServerSidePropsなどでfetchし、レンダリングまで済ませてユーザーに送信することができる。

export async function getServerSideProps() {
  const data = await fetch('https://api.com');
  return {
    props: { data },
  };
}
export default function Page({ data }) {
  return <>{data}</>;
}

しかし、これにも問題があって...
最上位のpageコンポーネントにおいてしか動かない。

例えば

  • 記事
  • お天気ウィジェット
  • 占いウィジェット

を表示するページがある時、各UI部品が必要とするデータをページレベルでまとめてfetchし、丁寧にUIまでバケツリレーすることになる。

Screenshot 2024-03-15 at 14.29.58.png

他のページでもお天気と占いを表示したくなれば、fetcherを独立させることになる。

Screenshot 2024-03-15 at 14.31.55.png

データはページと一致するものではない。それを必要とするUIと結合しているものだ。
この例で、お天気fetcherの役目はお天気コンポーネントへのデータ提供だが

  • コンポーネントは呼び出されるページと密結合している。このテンプレに占いを入れよう!という作業の影響範囲はページから占いコンポーネントまでの全てにわたる
  • バケツリレーは長い
  • フォルダは遠く、一緒に作業し辛い

千本引きの紐に頑張ってラベルをつけてる気持ちになる。

App Routerの特徴

App Router最大の特徴は、React Server Componentsが使えることだ。

結論だけ言えば、扱い方は以下になる。

  • デフォルトで全てのコンポーネントはRSCである。
  • RSCとは「ハイドレーションされない=静的HTMLになる代わりにサーバーサイドfetchができる」コンポーネントである
  • ハイドレーションが必要になった階層で 'use client;'で「クライアント境界」を設定する。そこから下層は全て標準コンポーネント(クライアントコンポーネント)になる。

繰り返しになるが、RSCの現状ほぼ唯一かつ大きな強みは「fetchができること」だ。

ハイドレーションが必要な代表的機能

App Routerでの構造

晴れて好きなコンポーネントでfetchできるようになったので、コンポーネントは自身が必要なデータの取得まで担当するのが一番だ。
「お天気を取得し表示する」
「ユーザーIDだけ受け取ったら残高を取得し表示する」
などである。

Screenshot 2024-03-15 at 15.15.02.png

部品の独立性は一気に高まり、コードの改修は容易になる。

作業としては、fetchをUIと一致する階層まで「下ろす」構造転換になる。
なお、fetchしたい階層とハイドレーションしたい階層が同じなら、新しく「fetch層」「ハイドレーション層」の2層に分けると良い。

//components/Otenki/index.tsx
import UIComponent from './UIComponent'

const DataComponent = () => {
  const data = await fetch('https://api.com');
  return <UIComponent data={data}/>;
};
//components/Otenki/UIComponent/index.tsx
'use client';
import { useState } from 'react';

const UiComponent = ({ data }) => {
  const [state,setState]= useState();
  //なんでもする
  return <>{state}</>
};

階層が増えてしまうが、千本くじより良い。

雑記

  • styled-componentsなどのCSS-in-JSライブラリのほとんどはRSCに対応する気配がない。スタイルを1行書けばそれ以下ではfetchできないので、大きなマイナスである。Next.js使いは離れる時かもしれない...
  • RSCはReactの機能だが、現在まともに使えるのはNext.js App Routerだけなので、Next.jsの機能と考えて問題ない
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2