Almost every screen loads data, and the boilerplate is always the same: three pieces of state (data, error, loading), an effect to kick off the request, and — the part people forget — a guard so that when the URL changes fast, a slow earlier response can't clobber a newer one. useFetch packages all of that: pass a URL, get back { data, error, loading } that refetches when the URL changes and ignores stale replies.
Implement useFetch(url, options). It fetches JSON and returns { data, error, loading }. It refetches whenever url changes, treats a non-2xx response as an error, and discards any response that arrives after the URL has moved on or the component has unmounted.
function useFetch(url, options) {
// returns { data, error, loading }
}
function User({ id }) {
const { data, error, loading } = useFetch(`/api/users/${id}`);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <Profile user={data} />;
}
// Typing fast in a search box changes the url rapidly; only the
// response for the CURRENT url is allowed to set state.
const { data } = useFetch(`/search?q=${query}`);
loading true while in flight; on success set data and clear error; on failure set error and clear data.fetch only rejects on network failure, so check response.ok and throw for HTTP errors like 404/500.url; a falsy url should do nothing.