Writing Your Own useFetch Hook in React

Writing Your Own useFetch Hook in React:

 

React Hooks have been all the rage for a little over a year. Let’s see how we can roll our own useFetch hook to abstract fetch request logic out of our components.

Note: This is for academic purposes only. You could roll your own useFetch hook and use it in production, but I would highly recommend using an established library like use-http to do the heavy lifting for you!

If you enjoy this post, please give it a 💓, 🦄, or 🔖 and consider signing up for 📬 my free weekly dev newsletter

Our UseFetch Function Signature

To determine our useFetch function signature, we should consider the information we might need from the end user to actually execute our fetch request. In this case, we’ll say that we need the resource url and we need the options that might go along with the request (e.g., request method).

function useFetch(initialUrl, initialOptions) {
  // Hook here
}

In a more full-featured solution, we might give the user a way ot abort the request, but we’re happy with our two arguments for now!

Maintaining State In Our Hook

Our hook is going to need to maintain some state. We will at least need to maintain url and options in state (as we’ll need to give our user a way to setUrl and setOptions). There are some other stateful variable’s we’ll want as well!

  • data (the data returned from our request)
  • error (any error if our request fails)
  • loading (a boolean indicating whether we are actively fetching)

Let’s create a bunch of stateful variables using the built-in useState hook. also, we’re going to want to give our users the chance to do the following things:

  • set the url
  • set options
  • see the retrieved data
  • see any errors
  • see the loading status

Therefore, we must make sure to return those two state setting functions and three data from our hook!

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  // Some magic happens here

  return { data, error, loading, setUrl, setOptions };
}

Importantly, we default our url and options to the initialUrl and initialOptions provided when the hook is first called. Also, you might be thinking that these are a lot of different variables and you’d like to maintain them all in the same object, or a few objects—and that would be totally fine!

Running An Effect When Our URL Or Options Change

This is a pretty important part! We are going to want to execute a fetch request every time the url or options variables change. What better way to do that than the built-in useEffect hook?

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // Fetch here
  }, [url, options]);

  return { data, error, loading, setUrl, setOptions };
}

Calling Fetch With Async Await

I like async/await syntax over Promise syntax, so let’s use the former! This, of course, works just as well using thencatch, and finally rather than async/await.

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    setError(undefined);

    async function fetchData() {
      try {
        const res = await fetch(url, options);
        const json = await res.json();
        setData(json);
      } catch (e) {
        setError(e);
      }
      setLoading(false);
    }
    fetchData();
  }, [url, options]);

  return { data, error, loading, setUrl, setOptions };
}

That was a lot! Let’s break it down a bit. When we run our effect, we know that we’re starting to fetch data. Therefore we set our loading variable to true and we clear our any errors that may have previously existed.

In our async function, we wrap our fetch request code with a try/catch block. Any errors we get we want to report to the user, so in our catch block we setError to whatever error is reported.

In our try block, we do a fairly standard fetch request. We assume our data being returned is json because I’m lazy, but if we were trying to make this the most versatile hook we would probably give our users a way to configure the expected response type. Finally, assuming all is successful, we set our data variable to our returned JSON!

Using The Hook

Believe it or not, that’s all there is to creating our custom hook! Now we just need to bring it into a sample app and hope that it works.

In the following example, I have an app that loads any github user’s basic github profile data. This app flexes almost all the features we designed for our hook, with the exception of setting fetch options. We can see that, while the fetch request is being loaded, we can display a “Loading” indicator. When the fetch is finished, we either display a resulting error or a stringified version of the result.

We offer our users a way to enter a different github username to perform a new fetch. Once they submit, we use the setUrl function exported from our useFetchhook, which causes the effect to run and a new request to be made. We soon have our new data!

const makeUserUrl = user => `https://api.github.com/users/${user}`;

function App() {
  const { data, error, loading, setUrl } = useFetch(makeUserUrl('nas5w'));
  const [user, setUser] = useState('');

  return (
    
      Find user:
      
{ e.preventDefault(); setUrl(makeUserUrl(user)); setUser(''); }} > { setUser(e.target.value); }} /> Find

{loading ? 'Loading...' : error?.message || JSON.stringify(data)}

); }

Concluding Thoughts

Writing a custom React hook can be a fun endeavor. It’s sometimes a bit tricky at first, but once you get the hang of it it’s quite fun, and can result in really shortening and reducing redundancy in your component code.

from Tumblr https://generouspiratequeen.tumblr.com/post/639560226943762432

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s