😺
データをfetchするときに便利なReactカスタムフック
fetchしたデータを使ってコンポーネントを描画する
fetchしたデータをもとに描画するとき、SWR
がとても便利です。
カスタムフックの実装
SWR
を使うと、fetchしたデータをキャッシュしてくれます。
また、ブラウザのタブを切り替えたときには自動でデータの再検証をしてくれたり、あるいはmutate
を使って任意のタイミングでデータを再検証できます。
useFetchData.ts
import useSWR, { mutate } from 'swr';
import { useCallback } from 'react';
export const useFetchData = (
startFetch: boolean,
) => {
const key = 'some_unique_key';
const { data, error } = useSWR(startFetch ? key : null, () =>
axios.get('your_endpoint_here')
);
const refetchData = useCallback(() => {
mutate(key);
}, [key]);
return {
data,
isLoading: !data && !error,
isError: !!error,
refetchData
};
};
コンポーネントの実装
startFetch === true
になればfetchが開始されます。
データをfetchしている最中はisLoading === true
となり、これを使ってロード中の表示ができます。
fetchはもちろん非同期的に行われますが、{ data, isLoading, refetchData }
ではresolveされた値が返ってくるので、コンポーネント側では非同期を意識する必要はありません。
また、refetchData
でデータを再検証することができます。
これがあればuseEffect
を使ったデータの再検証を代替できますね。
component.tsx
import { useFetchData } from './useFetchData';
export const SomeComponent: React.FC<{}> = ({}) => {
const [startFetch, setStartFetch] = useState(false);
const { data, isLoading, refetchData } = useFetchData(startFetch);
return (
<>
<Button onClick={() => {setStartFetch(true)}}>フェッチ開始</Button>
<Button onClick={() => {refetchData()}}>データ再検証</Button>
{isLoading && <LoadingComponent />}
{!isLoading && data && <ComponentToShowData data={data}/>}
</>
);
};
任意のイベントが起きた時だけデータをfetchしたいとき
たとえば、ボタンを押した時にfetchを開始して何らかの処理をする場合を考えます。
上記のSWR
を使ったユースケースと異なるのは、「ボタンを押した時だけ」 APIをcallする、という点です。
SWR
はとても便利ですが、fetchしたデータはキャッシュされ、さらにブラウザのタブを切り替えた時などに自動でデータ再検証(APIの再call)が行われてしまいます。
上記が不要なケースでは下記のようにカスタムフックを書くことができます。
(SWRでも自動再検証のキャンセルができるようです)
カスタムフックの実装
useFetchData.ts
import { useCallback, useState } from 'react';
type Data = {
something: string;
}
export const useFetchData = () => {
const [dataState, setDataState] = useState<{
data: Data | null;
isLoading: boolean;
}>({
data: null,
isLoading: false
});
const fetchData = useCallback(async () => {
setDataState({
data: null,
isLoading: true
});
const data = await axios.get('your_endpoint_here')
setDataState({ data, isLoading: false });
return data;
}, []);
const fetchDataAndDoSomething = useCallback(async () => {
const data = await fetchData();
// do something here
}, []);
return { ...dataState, fetchDataAndDoSomething };
};
コンポーネントの実装
component.tsx
import { useFetchData } from './useFetchData';
export const SomeComponent: React.FC<{}> = ({}) => {
const [startFetch, setStartFetch] = useState(false);
const { data, isLoading, fetchDataAndDoSomething } = useFetchData();
return (
<>
<Button onClick={fetchDataAndDoSomething}>
'Fetch data and do something'
{isLoading && <LoadingComponent />}
</Button>
</>
);
};
参考
Discussion