発生した問題
個人開発したアプリのGETリクエストはすべてSWRを使ってfetchしたのですが、fetcher関数内で早期returnしているはずなのに出来ていなくて意図するレスポンスが得られないということが起こりました。
useFetch.ts
import useSWR from "swr";
import { useSupabaseSession } from "@/app/_hooks/useSupabaseSession";
export const useFetch = <T>(path: string) => {
const { token, isLoading } = useSupabaseSession();
const fetcher = async () => {
if(isLoading) return;
if (!token) return;
const prams = {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: token,
},
};
const resp = await fetch(`/api/${path}`, prams);
if (!resp.ok) {
const errorData = await resp.json();
throw new Error(
errorData.message || "An error occurred while fetching the data."
);
}
const data: T = await resp.json();
return data;
};
const results = useSWR(`/api/${path}`, fetcher);
return results;
};
このカスタムフックの下記の部分ですが、
if(isLoading) return;
if (!token) return;
セッション情報の取得が終わり、tokenが存在する状態で初めてfetch処理をしたいのですが、なぜかtokenが存在しない状態でfetch処理が実行されてしまいました。
原因(?)
詳しいことはわからないのですが、理由はSWRの仕様上、一度400(error)が返ってきた後に再度リクエストが走って200になった場合、errorの中身が残っていてdataとerror両方に値が入っていることが起こり得て、それが原因になっていた可能性がありました。
実際に条件がそろっていないのに実行されてしまい意図しない挙動をしてしまうことがありました。
条件付きフェッチ
条件付きフェッチを教えていただいて即解決しました。
useFetch.ts
import useSWR from "swr";
import { useSupabaseSession } from "@/app/_hooks/useSupabaseSession";
export const useFetch = <T>(path: string) => {
const { token, isLoding } = useSupabaseSession();
const shouldFetchData = !isLoding && token;
const fetcher = async () => {
if (!token) return;
const prams = {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: token,
},
};
const resp = await fetch(`/api/${path}`, prams);
if (!resp.ok) {
const errorData = await resp.json();
throw new Error(
errorData.message || "An error occurred while fetching the data."
);
}
const data: T = await resp.json();
return data;
};
const results = useSWR(shouldFetchData ? `/api/${path}` : null, fetcher);
return results;
};
useSWRの第一引数に三項演算子が使えて、条件がそろっていない時はエンドポイントではなくnullを渡すとfetcherは実行されません。
これですべて解決しました!!
下記、公式のサンプルコードです。
// 条件付きでフェッチする
const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
// ...または、falsyな値を返します
const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
// ...または、user.id が定義されてない場合にスローします
const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)
公式によると三項演算子以外にも条件付きフェッチする書き方はあるみたいですが、私は好みで三項演算子を使って書きました。