💾

[React]Request MemoizationをGraphQLで使いたい!

2024/10/10に公開

はじめに

大学の1コマより開発の時間のほうが楽しくて,感じる時間が違うたくみです.

Next.js(App Router) + GraphQLという構成で,Request Memoizationが効かなかったことがあったので,調査の結果をまとめます.

TL;DR

App Router + GrpahQLを使用するときは,fetch関数をcacheで囲みましょう.

失敗例

以下のようなfetch関数を使用して,GraphQLクエリを叩くだけのfetcherがあるとします.

fetcher.ts
import "server-only";

export const getData = async () => {
  try {
    const response = await fetch("http://localhost/graphql", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        query: `query getData() {
          getData() {
            name
          }
        }`
      }),
    }
  } catch (error) {
    console.error(error);
    throw error;
  }
}

これを以下のコンポーネントで使用します.
内容としては,Child1Child2というコンポーネントで同じgetData()という関数(内部でfetchを使用している関数)を呼び出しているという状況です.

Parent.tsx
export const Parent = () => {
  return (
    <>
      <Child1 />;
      <Child2 />;
    </>
  );
}
Child1.tsx
export const Child1 = async () => {
  const data = await getData();
  ...
}
Child2.tsx
export const Child2 = async () => {
  const data = await getData();
  ...
}

理想と現実

上記のコードを実行したときに,Request Memoizationが動作し、localhost/graphqlに対するリクエストは1回になってほしいですよね.
しかし,上記のコードだと,2回,つまりgetDataの呼び出しの数分だけリクエストが飛び,Request Memoizationが効きません!

原因と対策

原因

原因というほどではありませんが,なんとRequest MemoizationはPOSTでは動作しません!!

Memoization only applies to the GET method in fetch requests.

じゃあGraphQLを使用したい場合はどうするのが良いのか...

そもそもApp RouterはGraphQLを使用する必要がないみたいな話は聞きますが,GraphQL大好き人間なので使用しないという手はありません.

対策

じゃあどうしろと.
なんと,Reactにはcacheという関数があり,この関数で返り値をメモ化できるものがあります!

https://nextjs.org/docs/app/building-your-application/caching#react-cache-function

以下のようなfetch関数を定義すれば行けるっぽいです.

fetcher.ts
import "server-only";
import { cache } from "react";

export const getData = cache(async () => {
  try {
    const response = await fetch("http://localhost/graphql", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        query: `query getData() {
          getData() {
            name
          }
        }`
      }),
    }
  } catch (error) {
    console.error(error);
    throw error;
  }
});

最後に

るんるんでNext.jsをリファクタしていたときに起こった不具合でしたが,よくドキュメントを見てみればちゃんと書いてある内容でした.
みなさんもドキュメントを読むクセをつけて,良いApp Routerライフを!!

参考

https://nextjs.org/docs/app/building-your-application/caching#request-memoization

ユニフォームネクスト株式会社

Discussion