【React Hooks】 APIでデータの再取得を行うフックの実装方法の考察
フロントエンドエンジニアの茶木です。
APIでデータの再取得を行うフックの実装方法を考察しています。
再取得機能のない getApi のフックの実装と使用例
useGetApi
まず、単純に APIでのデータ取得をコンポーネントで扱いやすくするフックの実装が以下です。
引数の getApi には async() => await axios.get("path")
が入る想定です。
コンポーネントマウント時だけ、useEffect 内で geApi がコールされてデータの取得が行われます。
import { AxiosResponse } from "axios";
const useGetApi = <Res>(
getApi: () => Promise<AxiosResponse<Res, undefined>>
): Res | undefined => {
const [data, setData] = useState<Res>();
useEffect(() => {
getApi().then((res) => setData(res.data));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return data;
};
API
今回は https://open-meteo.com/ 天気予報のAPIから情報を取得してみようと思います。
定義したFORECAST は、APIで取得できるデータの型で current_weather
は現在の天候情報です。
import axios from "axios";
interface FORECAST {
current_weather: {
weathercode: number;
temperature: number;
time: string;
};
}
export const api = async () =>
await axios.get<FORECAST>(
"https://api.open-meteo.com/v1/forecast?latitude=35.6894&longitude=139.6917¤t_weather=true&timezone=Asia/Tokyo"
);
呼び出し
フックを使い取得した data を加工して表示します。data が undefined
のときは取得前です。
export default function Index(): ReactElement | null {
const data = useGetApi<FORECAST>(api);
if (!data) {
return (
<p>読み込み中...</p>
);
}
return (
<div>
<h1>Forecast</h1>
<h2>{getWeatherLabel(data.current_weather.weathercode)}</h2>
<p>気温: {data.current_weather.temperature}℃</p>
<p>
<small>{data.current_weather.time} 時点の天気</small>
</p>
</div>
);
}
このように、天候、気温と、時間が取得できます。
再取得機能をもたせたフック
reloadCount を使った再取得機能
ふまえて、この useGetApi
を改修して再取得機能をつけます。
import { AxiosResponse } from "axios";
export const useReloadableApi = <Res>(
getApi: () => Promise<AxiosResponse<Res, undefined>>
): {
data: Res | undefined,
reload: () => void
} => {
const [reloadCount, setReloadCount] = useState(0);
const [data, setData] = useState<T | undefined>(undefined);
const reload = useCallback(() => setReloadCount((prev) => prev + 1), []);
useEffect(() => {
getApi().then((res) => setData(res.data));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [reloadCount]);
return { data, reload };
}
機能解説
コンポーネントマウント時には、useEffect 内部の getApi
がデータを取得します。また、reloadCount
の更新があったときでも再描画の処理が発生し同様にデータを取得します。reloadCount
の更新は、reload
メソッドによって起きるので、この reload メソッドを button の onClick
などにつけて呼び出せば、再度 APIをリクエストして情報を取得しなおします。
useStateを使った再描画のリクエスト
useState
を使ったコンポーネントの再描画(ここでの命名は reloadCount
, setReloadCount
)は、要するに state
の変更を意図的に起こして再描画させる手法で、おそらく一般的なものだと思います。
別解: reloadCount を使わない方法
export const useReloadableApi = <Res>(
getApi: () => Promise<AxiosResponse<Res, undefined>>
): {
data: Res | undefined,
reload: () => void
} => {
const [data, setData] = useState<Res>();
const reload = useCallback(
() => getApi().then((res) => setData(res.data)),
[getApi]
);
useEffect(() => {
reload();
}, [reload]);
return [data, reload];
};
ただ、APIのデータ再取得と再描画に関しては、useState
を使わず、reload
内に直接 APIを組み入れてしまう方がより良い方法だと考えています。
reloadCount
を使わない方が良いと考える理由は2つです
- 再描画を起こすためだけの
setState
の使用は可読性を下げる reload
のコール時に、reloadCount
を使う方が1回再描画が多いreload
コールのタイミングでreloadCount
の更新時に1度- APIの取得完了タイミングで
data
の更新時に1度 再描画が発生する
特に後者については、1度目の再描画の data
は、更新前の data
と同一のため、この data
を使用するコンポーネントが適切に実装されていれば、普通は問題は起きないのですが、それでも上位コンポーネントでコントロールできるものについては、コントロールしていくほうが良いと考えます。
おわりに
この他にも、取得したデータをもとにユーザーが変更し、その変更を一時的に保持する必要があるといったケースなど API 関連のフックはバリエーションに富んでいます。ほかにもこのようなフックを紹介していきます。
Gaji-Labo は Next.js, React, TypeScript 開発の実績と知見があります
フロントエンド開発の専門家である私たちが御社の開発チームに入ることで、バックエンドも含めた全体の開発効率が上がります。
「既存のサイトを Next.js に移行したい」
「人手が足りず信頼できるエンジニアを探している」
「自分たちで手を付けてみたがいまいち上手くいかない」
フロントエンド開発に関わるお困りごとがあれば、まずは一度お気軽に Gaji-Labo にご相談ください。
オンラインでのヒアリングとフルリモートでのプロセス支援にも対応しています。
Next.js, React, TypeScript の相談をする!