🫥
React + fetch + AbortController の最小構成
Reactコンポーネントにfetch機能を実装したい。かつ、任意のタイミングでキャンセルできるようにしたい。
…を実現するためのサンプルコードになります。
(正直、何かしらのfetchライブラリを使えば、もっと簡単に実現できる気がしますが。。)
ソースコード
Sample.tsx
import { useCallback, useEffect, useRef, useState } from 'react';
export const Sample = () => {
const [data, setData] = useState('');
const [isLoading, setLoading] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null); // … ①
/**
* データフェッチ関数
*/
const fetchData = useCallback(async (url: string) => {
if (abortControllerRef.current) {
abortControllerRef.current.abort(); // … ②
}
const controller = new AbortController();
abortControllerRef.current = controller; // … ③
setLoading(true);
await fetch(url, { signal: controller.signal }) // … ④
.then(async (response) => {
return await response.json();
})
.then((result) => {
setData(JSON.stringify(result));
})
.catch((error) => {
console.error(error);
})
.finally(() => {
setLoading(false);
});
}, []);
/**
* データフェッチをキャンセルする関数
*/
const cancelFetch = useCallback(() => { // … ⑤
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
}, []);
/**
* コンポーネントを破棄する際に、データフェッチを実行中であればキャンセルする
*/
useEffect(() => {
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, []);
return (
<>
<div>{isLoading ? 'Loading…' : data}</div>
<button
type="button"
onClick={() => {
fetchData('https://jsonplaceholder.typicode.com/todos/1');
}}
>
fetch data
</button>
<button type="button" onClick={cancelFetch}>
cancel fetch
</button>
</>
);
};
解説
ソースコードを見ての通り、ではありますが、少しだけ解説します。
- ①:
AbortController
を入れるための枠をuseRef
で用意します。 - ②:データフェッチ関数
fetchData
の中身を見ていきます。- まず②のところで、直前に実行していた
fetch
があれば、キャンセルします。これで常に1本だけのfetch
が実行できるようになりますね。
- まず②のところで、直前に実行していた
- ③:
abortControllerRef
に、新規のAbortController
を入れ直します。新しく実行するfetch
用のコントローラーになりますね。 - ④:
fetch
のパラメータのsignal
にcontroller.signal
を設定します。このあとは通常通り、fetch
が実行されていきます。 - ⑤:
fetch
が実行中に、データフェッチをキャンセルする関数cancelFetch
を任意のタイミングで呼び出せば、fetch
をキャンセルできます。- データフェッチをキャンセルする関数
cancelFetch
の中身は、単にabort()
を実行しているだけですね。
- データフェッチをキャンセルする関数
まとめ
以上、React + fetch + AbortController の最小構成でした。
fetch系ライブラリを使えば、一発で実装できる機能かと思いますが、ライブラリを使わずにライトに作りたい時に重宝しそうです。
Discussion