DEV Community

React: Creating a Custom Hook for Fetching Data

Sebastian on March 08, 2020

Fetching data from an external or internal API is a common use case for web applications. With react functional components, there are different hoo...
Collapse
 
pavermakov profile image
Pavel Ermakov

Awesome!

What about the case where you make an async request and immediately switch to another screen? React will complain that it cannot re-render an unmounted component or something like that. Is there a way to cancel the request?

Collapse
 
admantium profile image
Sebastian

I think this is a question for Thành Trang?

Collapse
 
trangcongthanh profile image
Thành Trang • Edited

My sugesstion:

The hook should take a promise as param. It will more flexible than take url

const useFetchData = (promiseFn) => {
...
const invoke = async () => {
...
const res = await promiseFn()
...
}
Enter fullscreen mode Exit fullscreen mode

You can use fetch, axios, or any HTTP client library.

Fetch ex.:

const response = useFetchData(fetch('url', { method: 'POST' }).then(res => res.json))
Enter fullscreen mode Exit fullscreen mode

I prefer return type same as React.useState api.

return [{ data, loading, error }, invoke]
Enter fullscreen mode Exit fullscreen mode

More easier for naming.
Alot of case, you will need more than 2 api calls. So you can:

const [api1, api1InvokeFn] = useFetchData()
const [api2, api2InvokeFn] = useFetchData()

React.useEffect(() => {
// call api1 when mounted
api1InvokeFn()
}, [api1InvokeFn])

<button onClick={api2InvokeFn}>Fetch</button>

if (api1.loading) {
... show loading indicator
}
if (!api2.loading && api2.data) {
... do something data
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
admantium profile image
Sebastian

Using promises improves the flexibilty of your app, in my case I'm happy to stay with the synchronous call and having a timeout barrier.

Collapse
 
notrab profile image
Jamie Barton

Nice read Sabastian!

I recently inherited a project that had something similar, and, it took me quite a bit of time to get familiar with the magic it had abstracted. Your's looks a lot easier to follow to say the least.

swr.now.sh is also a great alternate that takes care of some additional magic if anyone reading doesn't feel like maintaining their own fetch hook.

Collapse
 
admantium profile image
Sebastian

Hi Jamie, that's a good link you provided. Indeed there are several fetch hooks on Github and other sources. My article is primary a tutorial to get into hooks and understand them. Once familiar, more complex implementations and examples become accessible.

Collapse
 
slinden2 profile image
slinden2 • Edited

Shouldn't the load function be in a useCallback hook? When you call it from a useEffect hook, you must add the load function into the dependency array. If you add it, that causes an infinite loop as the load function gets recreated on every render.



const load = useCallback(async () => {
    init();
    setLoading(true);
    try {
      const result = await axios.fetch(url, {timeout: timeout}).data;
      setData(result);
    } catch (e) {
      setError(true);
    }
    setLoading(false);
}, [url])
Collapse
 
aspforyou profile image
Joseph K. Ryan

where is the repo?

Collapse
 
admantium profile image
Sebastian

Hi Jospeh, I don't yet publish code on Github, but you can see the final component code above.