はじめに
前ステップから続き、勉強用のuseFetchを書いていきます。今回のテーマはfetchのオプションです。
課題
これまでは、単純なHTTP GETのみを想定してきましたが、HTTP POSTやヘッダー指定などが必要となる場面もあるでしょう。Fetch APIには、オプションが渡せるようになっています。また、これまではJSONを取得することを前提としていましたが、JSON以外のデータも扱いたいかもしれません。
(今回は、ストリームまでは扱いません。hookでストリームをどう扱うかはより複雑な問題になりそうです。)
ステップ6: fetchオプション
const defaultOpts = {};
const defaultReadBody = body => body.json();
const useFetch = (
url,
opts = defaultOpts,
readBody = defaultReadBody
) => {
const [result, setResult] = useState({});
useEffect(() => {
let cleanedUp = false;
const abortController = new AbortController();
const fetchData = async () => {
try {
const response = await fetch(url, {
...opts,
signal: abortController.signal
});
if (!response.ok) throw new Error(`status: ${response.status}`);
const data = await readBody(response);
if (!cleanedUp) {
setResult({ data });
}
} catch (error) {
if (!cleanedUp) {
setResult({ error });
}
}
};
setResult({ loading: true });
fetchData();
const cleanup = () => {
cleanedUp = true;
abortController.abort();
setResult({});
};
return cleanup;
}, [url, opts, readBody]);
return result;
};
注意点としては、defaultOpts
とdefaultReadBody
は、useFetchの外側で定義してあることです。
動作確認
実際に動くコードはこちらです。codesandbox
opts
を指定する場合は、コンポーネントやhookの外側で定義することが基本です。renderで新しいオブジェクトが作られると、useEffectが無限ループになります。propsに依存したオプションを設定したい場合はuseMemo
などでmemoizeする必要があります。(その場合はexhaustive-depsのルールでdepsが適切か確認しましょう。)
おわりに
本ステップでuseFetchの実装は完了しました。これをそのままでも、これをベースに修正を加えてでも、利用することができると思います。
こちらの記事で紹介したuseFetchはほぼ同じ機能を持ちますが、useReducerで実装されていることと、その他変数名などが多少異なります。好みで使い分ければ良いと思います。