🐷

React.cache を知らないと損する

2025/01/02に公開

結論

RSC で React.cache を知らないと損する

前提

なぜ?

React.cache使う場合使わない場合で比較したら一目瞭然

1. cache を使わない場合

React.cache を使わない場合のサンプルコードを以下に記す。

// app/page.tsx
export default async function HelloAgain1() {
  const currentUser = await loadCurrentUser();
  return (
    <div>
      HelloAgain1 {currentUser.name}
      <HelloAgain2 />
    </div>
  );
}

async function HelloAgain2() {
  const currentUser = await loadCurrentUser();
  return <div>HelloAgain2 {currentUser.name}</div>;
}

const loadCurrentUser = async () => {
  console.log("loadCurrentUser");
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return { id: "1", name: "Kimura" };
};

ページが読み込まれるまでの時間を
DevTools の NetWorkタブ で確認する。結果は??

2.06秒

2.06秒!
同じページ内で2回 loadCurrentUser が呼ばれているから当然!

2. cache を使う場合

React.cache を使う場合のサンプルコードを以下に記す。

// app/page.tsx
import { cache } from "react";

export default async function HelloAgain1() {
  const currentUser = await loadCurrentUser();
  return (
    <div>
      HelloAgain1 {currentUser.name}
      <HelloAgain2 />
    </div>
  );
}

async function HelloAgain2() {
  const currentUser = await loadCurrentUser();
  return <div>HelloAgain2 {currentUser.name}</div>;
}

const loadCurrentUser = cache(async () => {
  console.log("loadCurrentUser");
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return { id: "1", name: "Kimura" };
});

ページが読み込まれるまでの時間を
DevTools の NetWorkタブ で確認する。結果は??

1.08秒

1.08秒!

同じページ内で2回 loadCurrentUser がコード上では呼ばれているが、同じリクエストだから2回目のリクエストはキャッシュされていることが分かる

補足情報

Next.js の Request Memoization
セクションに以下の様に英語で記載されている

Good to know:

- Request memoization is a React feature, not a Next.js feature. It's included here to show how it interacts with the other caching mechanisms.
- Memoization only applies to the GET method in fetch requests.
- Memoization only applies to the React Component tree, this means:
  - It applies to fetch requests in generateMetadata, generateStaticParams, Layouts, Pages, and other Server Components.
  - It doesn't apply to fetch requests in Route Handlers as they are not a part of the React component tree.
- For cases where fetch is not suitable (e.g. some database clients, CMS clients, or GraphQL clients), you can use the React cache function to memoize functions.

⬇︎⬇︎⬇︎日本語に翻訳する⬇︎⬇︎⬇︎

知っておくとよいこと:

- リクエストのメモ化はReactの機能であり、Next.jsの機能ではありません。
- メモ化はフェッチリクエストのGETメソッドにのみ適用されます。
- メモ化はReactコンポーネントツリーにのみ適用されます。つまり
  - generateMetadata、generateStaticParams、Layouts、Pages、その他のServer Componentsのフェッチリクエストに適用されます。
  - ルートハンドラはReactコンポーネントツリーの一部ではないため、フェッチリクエストには適用されません。
- フェッチが適切でない場合(一部のデータベースクライアント、CMSクライアント、GraphQLクライアントなど)には、React.cache 関数を使用して関数をメモ化できます。

最後にもう一度結論

Request Memoization が
使用できる状況では React.cache を使おう!

特に GraphQL を使用しているプロジェクトや
fetch を使用していないプロジェクトでは必ず使おう!

参考文献

Discussion