I Made a Hook!
Tags: usefetch, react, hooks, fetch
Published: 2022-01-16 03:51:04

## Introduction

In 2021, you can't learn React without learning about Hooks. According to the authority, <a href="http://www.reactjs.org">React</a>:

> Hooks are functions that let you “hook into' React state and lifecycle features from function components. ...A _custom Hook_ is a JavaScript function whose name starts with ‘use’ and that may call other Hooks.

In other words, Hooks are functions that are able to use React features and syntax, most notably, the built-in Hooks `useState` & `useEffect`.

In my VERY early journey into learning React, Hooks have been useful to me for separating complex or distracting logic from React components into their own files/functions, which cleans up my components. I like this because it makes the more important logic standout and my components easier to comprehend overall. Refactoring custom Hooks into their own files also makes them reusable throughout my entire application. As my applications grow, this versatility will become more and more critical in keeping my code DRY and easy to comprehend.

One almost unavoidable task when building an app is requesting data with a fetch request. For my second of five projects in my coding bootcamp, I’m building a restaurant point-of-sales (POS) application. Important setup data, like a menu or a list of saved customers, are stored in a json file that requires a fetch request to retrieve and use. For this article, however, I'll be using the <a href="https://pokeapi.co/docs/v2">POKEAPI</a> in my examples, if only to make them easier to follow. You can check out my restaurant POS <a href="https://github.com/theFlexOne/pos-pizza">_here_</a> if you'd like.

First, I'll quickly explain how I make basic fetch requests in React, without using any custom Hooks. After that, I'll demonstrate how I created a custom Hook to do the same thing, as well as, go over their differences.

<br/><hr/>

## Making a basic fetch request (in React)

Below is an example of how to make a fetch request inside a React component and store the results/error within a piece of state.

```javascript
import { useState, useEffect } from 'react';

const POKEMON_URL = 'https://pokeapi.co/api/v2/pokemon';

export default function App() {
  const [pokemon, setPokemon] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    fetch(POKEMON_URL)
      .then(res => res.json())
      .then(pokeData => setPokemon(pokeData))
      .catch(err => {
        console.error(err);
        setError(err);
      })
      .finally(() => setIsLoading(false));
  }, []);

  // Example usage of pokemon data
  const pokemonCards = pokemon.results.map(poke => {
    // function logic...
  })

  return (
    // Conditional JSX template...
  );
}
```

I initiated three separate pieces of state to handle the fetch call:

- _pokemon_ – stores the value of a successful fetch request,
- _error_ – contains any errors that may occur,
- _isLoading_ – a boolean indicating whether or not a fetch request is currently taking place.

If the request is successful, the response is stored in `results`, otherwise, I have a `catch` function that stores the error in `error`, if the fetch fails. After the fetch is resolved, regardless of the results, I need to set `isLoading` back to false, which is handled inside `finally()` Since fetching data is asynchronous, meaning it takes some time to complete, `isLoading` gets set to true until the fetch either succeeds or fails, and is false while nothing is being fetched. Now, I can use the pokemon data I've fetched in the rest of my application just like any other piece of state.

As mentioned above, I find custom hooks useful for separating the logic inside my components into their own functions and/or files, leaving only the essence of what my component is intended for. This lightens up my component and makes comprehending it a lot easier. Also, my `useFetch` Hook can now be reused by any component, giving my fetch call more utility, as it’s no longer confined to only one component.

<br/><hr/>

## My custom Hook - `useFetch`

The block of code below does exactly the same thing as the previous code block, only the fetch logic is entirely inside of my `useFetch` Hook (function), including my three state variables. I've also added an `AbortController` to my Hook to "cleanup" my `useEffect` Hook.

_App.js_

```javascript
import { useState, useEffect } from 'react';

const POKEMON_URL = 'https://pokeapi.co/api/v2/pokemon';

const useFetch = () => {
  const [pokemon, setPokemon] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;
    setIsLoading(true);
    fetch(POKEMON_URL, { signal })
      .then(res => res.json())
      .then(pokeData => setPokemon(pokeData))
      .catch(err => setError(err))
      .finally(() => setIsLoading(false));
    return () => controller.abort();
  }, []);
  return { pokemon, isLoading, error };
};

export default function App() {
  const { pokemon, isLoading, error } = useFetch();

  // Example usage of pokemon data
  const pokemonCards = pokemon.results.map(poke => {
    // function logic...
  })

  return (
    // conditional JSX template...
  );
}
```

Moving my fetch logic into it's own custom Hook makes my component easier to understand. I think it's pretty obvious that `useFetch` is a function that makes a fetch call. Code that's easy to read is highly encouraged, from what I've gathered, and it makes sense, especially when collaborating with others. I hate when I have to reread my old code a few times to understand what it does.

Note: it’s always best for custom Hooks to start with the word ‘use’ followed by whatever you want (useFetch, useState, useEffect). This is to let other programmers and react know right away that the function is a react Hook. I'm probably oversimplifying it, so if you want to know more, you can check out the docs for React, <a href="https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook">_here_</a>.

<br/><hr/>

## Refactoring into `useFetch.js`

Technically, I did it. I made a custom Hook. It doesn't need to be refactored into a separate file to be a custom Hook (I only say that because that was my impression, at first), but doing so but doing so has a couple advantages for me. First, it will make my component MUCH cleaner and, more importantly, I can make it even more reusable, allowing me to import it into any custom Hook or component.

---

_App.js_

```javascript
import useFetch from '../hooks/useFetch';

const POKEMON_URL = 'https://pokeapi.co/api/v2/pokemon';

export default function App() {
  const {results: pokemon, isLoading, error} = useFetch(POKEMON_URL);

  // Example usage of pokemon data
  const pokemonCards = pokemon.results.map(poke => {
    // function logic...
  })

  return (
    // conditional JSX template...
  );
}
```

_useFetch.js_

```javascript
import { useState, useEffect } from 'react';

export default function useFetch(url) {
  const [results, setResults] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    const controller = new AbortController();
    const { signal } = controller;
    fetch(url, { signal })
      .then(res => res.json())
      .then(data => setResults(data))
      .catch(err => setError(err))
      .finally(() => setIsLoading(false));
    return () => controller.abort();
  }, [url]);

  return { results, isLoading, error };
}
```

I think that looks WAY cleaner and makes reading it MUCH easier. This way, I'm able to get rid of all of the `useFetch` logic in `App.js`, including my three state variables, as well as, my `useState` & `useFetch` imports. All I have to do is import `useFetch` at the top and invoke it, destructuring the my three state variables from it's return. Notice I've changed the name of the 'pokemon' state variable to 'results', to make it more universal. Notice I did, however, rename it to 'pokemon' when destructuring it inside `App.js`.

The logic inside of `useFetch.js` is basically cut right from `App.js`. The only difference is I've made it more dynamic by creating a 'url' parameter so I can use my Hook to call other endpoints, if necessary.

When creating custom Hooks, I always store them inside a folder called `hooks`, located just inside the `src` folder (`/src/hooks/useFetch.js`). This is the most common way to structure your hook files, from my experience.

![Folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75nx7rmj0m5u13fxv0cy.png)

It’s also best-practice for the file name and the Hook name to be the same. Also, you should export the function by default.

<br/><hr/>

## Conditional rendering

Once I've destructured my state variables from `useFetch()`, I can use them to conditionally output JSX based on the their current values.

From my `App.js` examples above...

```javascript
  return (
    // conditional JSX template...
  );
```

This portion of code is commented out to make my examples shorter and less overwhelming, but now I'll open it up and show one way to use state to conditionally output JSX.

Hopefully, your familiar with conditional rendering, in general, but if not, you can learn more about it <a href="https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper">_here_</a>.

<br/>

```javascript
return (
  <>
    {pokemon && !isLoading && (
      <div className="FetchHook">
        {pokemon.results.map((poke, i) => (
          <div key={i + 1}>{poke.name}</div>
        ))}
      </div>
    )}
    {isLoading && <div>Loading...</div>}
    {error && !isLoading && (
      {console.error(error)}
      <div>
        <h2>ERROR:</h2>
        <p>{JSON.stringify(error)}</p>
      </div>
    )}
  </>
);
```

Above, there's three possible templates being rendered, but none at the same time. First, I checked if the `pokemon` variable was truthy, meaning it wasn't empty, and I also made sure `isLoading` was set to false, meaning the fetch request was resolved. Currently, I'm simply outputting each pokemon's name inside a `div`, but if I were to take this further and make a pokedex, I could map through the results to create an interactive list of pokemon cards or whatever.

Second, whenever `isLoading` is true, I wanted to output a message saying so. This is where a loading spinner or a skeleton could be rendered.

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94nm3j3mle7jy301au5k.gif" height="75" />

And third, if `errors` is ever true, meaning there was an error while fetching the data, I wanted to output that information to let the user know, as well as, make sure `isLoading` was set back to false.

To bring this article full circle, because I extracted `useEffect` into it's own file, the rest of my `App` component can focus on it's intended purpose, which is to output a list of pokemon, making the code easier to comprehend on the first read-through.

<br/><hr/>

## More info on Hooks

Unlike React components, Hooks can have any return value, not just valid JSX. For example, `useState` returns a state variable and a function to set it, that is destructured from calling `useState()`. `useEffect`, on the other hand, doesn't return anything.

By definition (see above), Hooks can call other Hooks, including `useState`, meaning Hooks can initialize and manipulate state. That's part of what make custom Hooks so powerful! This also means that my three state variables can only be updated from within `useFetch.js`, unless I include their set functions in the return. There should never be a reason for me to update these variables from outside the file it lives in besides refetching the data, but if there ever is one, I can always go into `useFetch.js` and simply export whichever set function I need.

<br/><hr/>

## Conclusion

So with that... I made a Hook! If you want to know more about Hooks, custom Hooks, or React in general, the best place is at <a href="reactjs.org">reactjs.org</a>.

There were a few videos on YouTube that really helped me understand making custom Hooks, specifically making a custom `useFetch` Hook. In general, I owe these guys a lot for teaching me everything:

- <a href="https://www.youtube.com/watch?v=Vspeudp-M9k">"How to Fetch Data in React With A Custom useFetch Hook"</a> - from <a href="https://www.youtube.com/channel/UC8S4rDRZn6Z_StJ-hh7ph8g">PedroTech</a>
- <a href="https://www.youtube.com/watch?v=Jl4q2cccwf0">"Full React Tutorial #20 - Making a Custom Hook"</a> - from <a href="https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg">The Net Ninja</a>
- <a href="https://www.youtube.com/watch?v=qzi-X3quu3w&t=714s">"React 2021 Custom Hooks with LocalStorage & axios - Episode 18"</a> - from <a href="https://www.youtube.com/channel/UCTBGXCJHORQjivtgtMsmkAQ">Steve Griffith - Prof3ssorSt3v3</a>

If you've made it this far, thank you, sincerely, for reading my article. This is only the third blog post I've ever made, so if I've made any huge mistakes or you want to give me advice on how to do this better, please, let me know!

<br/>

That's it.

<br/>

\- END -