Skip to content

Instantly share code, notes, and snippets.

@jamesplease jamesplease/0.usage.js
Last active Mar 12, 2019

Embed
What would you like to do?
A general-purpose fetch hook. This forms the foundation of the Standard Resource fetch hook.
import useFetch from './use-fetch';
export default function book({ bookId }) {
const {
// wow so much stuff
fetching,
failed,
succeeded,
data,
res,
error,
abortController,
abortSignal,
// Call this to make the request yourself
request
} = useFetch(
// This is a function that actually makes the network request.
// Note that `fetchBooks` **must** always resolve!
() => fetchBooks(bookId),
// This does two things: it makes the fetcher automatically get called on mount,
// and also get called _again_ anytime that this value changes.
[bookId],
{
// When the 2nd argument is passed, you can prevent fetches by passing false as this
// value. In this situation, we won't fetch the books when the ID isn't a valid number
okayToFetch: Number.isNaN(bookId),
// These are probably dumb options that likely don't need to exist
abortPrevious,
abortOnUmount
}
);
// Because there is no second arg, this one is not called on mount (nor will it ever be
// called automatically for you). You must manually call it using `request()`
const [request] = useFetch(() => deleteBook(bookId));
}
import { useState, useEffect, useRef } from 'react';
const defaultFetchStatus = {
data: null,
res: null,
failed: false,
succeeded: false,
error: null,
};
export default function useFetcher(
fn,
resolveCondition,
// TODO: think about this API...`okayToFetch` is going to be much more common
// than everything else.
// Also, I need to accept an AbortController factory.
{ okayToFetch = true, abortPrevious = true, abortOnUnmount = true } = {}
) {
const automaticallyFetch = Array.isArray(resolveCondition);
const [fetchResults, setFetchResults] = useState({
...defaultFetchStatus,
fetching: automaticallyFetch,
});
const abortControllerRef = useRef();
const request = () => {
if (!okayToFetch) {
return;
}
if (abortPrevious) {
if (
fetchResults.abortController &&
typeof fetchResults.abortController.abort === 'function'
) {
fetchResults.abortController.abort();
}
}
const abortController = new AbortController();
abortControllerRef.current = abortController;
let isPreviousRequest = false;
setFetchResults({
...fetchResults,
data: null,
fetching: true,
abortController,
abortSignal: abortController.signal,
});
const options = {
signal: abortController.signal,
};
fn(options).then(result => {
abortControllerRef.current = null;
if (isPreviousRequest) {
return;
}
setFetchResults({
data: result.res ? result.res.data : null,
res: result.res,
fetching: false,
failed: !result.success,
succeeded: result.success,
error: result.err,
abortController: null,
abortSignal: null,
});
});
return () => {
isPreviousRequest = true;
};
};
if (automaticallyFetch) {
useEffect(request, resolveCondition);
}
// This code supports aborting the request when the component unmounts
useEffect(() => {
return () => {
if (abortOnUnmount) {
if (
abortControllerRef.current &&
typeof abortControllerRef.current.abort === 'function'
) {
abortControllerRef.current.abort();
}
}
};
}, []);
return {
...fetchResults,
request,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.