Skip to content

Instantly share code, notes, and snippets.

@FredrikAugust
Last active September 3, 2021 11:46
Show Gist options
  • Save FredrikAugust/9fde8b569477d9e5b3de3a4d1f724e99 to your computer and use it in GitHub Desktop.
Save FredrikAugust/9fde8b569477d9e5b3de3a4d1f724e99 to your computer and use it in GitHub Desktop.
useFetch with cancellation token
import { useEffect, useState } from 'react';
export type UseFetchReturnType<T> = {
loading: boolean;
data?: T;
error?: Error;
};
type FetchParameters = Parameters<typeof fetch>;
/**
* Helper function to fetch resources from an API. Uses AbortControllers to ensure that fetch
* calls are aborted when the component which invoked the call is unmounted. This avoids
* "performing operations on an unmounted component" errors due to fetch calls.
*
* @param url RequestInfo or string url to fetch
* @param options Options passed to fetch. Signal can be overridden by specifying it here.
* @param dependencies Optional list of dependencies. When these change, the request will be re-retrieved.
*/
export default function useFetch<T>(
url: FetchParameters[0],
options: FetchParameters[1] = {},
dependencies: Array<unknown> = [],
): UseFetchReturnType<T> {
const [loading, setLoading] = useState(true);
const [data, setData] = useState<T>();
const [error, setError] = useState<Error>();
const abortController = new AbortController();
useEffect(() => {
(async () => {
try {
const response = await fetch(url, { signal: abortController.signal, ...options });
const data = await response.json();
setData(data);
setLoading(false);
} catch (e) {
if (e instanceof DOMException) {
// aborted fetch, don't update UI
// this will cause update after unmount exceptions
return;
}
setLoading(false);
setError(e);
}
})();
return () => abortController.abort();
}, dependencies);
return { loading, data, error };
}
@FredrikAugust
Copy link
Author

FredrikAugust commented Sep 3, 2021

Example usage

const Fish = (props) => {
  const { data, loading, error } = useFetch(`/fish-details/${props.fishID}`);

  if (loading) return (<p>We are loading</p>);

  if (error) return (<p>Unintentional feature detected</p>);

  return (<p>{data?.fishName}</p>);
};

@FredrikAugust
Copy link
Author

FredrikAugust commented Sep 3, 2021

Slightly more complex example

const { loading, data } = useFetch<FishDetails>(
    `/fish/${fishID}`,
    { method: 'POST', body: JSON.stringify({ fishName: 'John the Fish' }) },
    [props.fishes],
);

@FredrikAugust
Copy link
Author

By default it runs a GET request once per mount

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment