[React]Request MemoizationをGraphQLで使いたい!
はじめに
大学の1コマより開発の時間のほうが楽しくて,感じる時間が違うたくみです.
Next.js(App Router) + GraphQLという構成で,Request Memoizationが効かなかったことがあったので,調査の結果をまとめます.
TL;DR
App Router + GrpahQLを使用するときは,fetch関数をcache
で囲みましょう.
失敗例
以下のようなfetch関数を使用して,GraphQLクエリを叩くだけのfetcherがあるとします.
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;
}
}
これを以下のコンポーネントで使用します.
内容としては,Child1
,Child2
というコンポーネントで同じgetData()
という関数(内部でfetch
を使用している関数)を呼び出しているという状況です.
export const Parent = () => {
return (
<>
<Child1 />;
<Child2 />;
</>
);
}
export const Child1 = async () => {
const data = await getData();
...
}
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関数を定義すれば行けるっぽいです.
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ライフを!!
参考
Discussion