Custom Hooks

For now, we're going to make a custom hook of our own. Just like useState is a hook, there are a few others like useEffect (which we'll use in this lesson), useReducer (for doing Redux-like reducers), useRefs (for when you need to have programmatic access to a DOM node), and useContext (for using React's context which we'll do shortly as well.) But like React hooks, we can use these hooks to make our re-usable hooks.

We need a list of breeds based on which animal is selected. In general this would be nice to request once and if a user returns later to the same animal, that we would have some cache of that. We could implement in the component (and in general I probably would, this is overengineering it for just one use) but let's make a custom hook for it.

Make a new file called useBreedList.js in src and put this in it.

import { useState, useEffect } from "react";

const localCache = {};

export default function useBreedList(animal) {
  const [breedList, setBreedList] = useState([]);
  const [status, setStatus] = useState("unloaded");

  useEffect(() => {
    if (!animal) {
      setBreedList([]);
    } else if (localCache[animal]) {
      setBreedList(localCache[animal]);
    } else {
      requestBreedList();
    }

    async function requestBreedList() {
      setBreedList([]);
      setStatus("loading");
      const res = await fetch(
        `http://pets-v2.dev-apis.com/breeds?animal=${animal}`
      );
      const json = await res.json();
      localCache[animal] = json.breeds || [];
      setBreedList(localCache[animal]);
      setStatus("loaded");
    }
  }, [animal]);

  return [breedList, status];
}
  • We're using hooks inside of our custom hook. I can't think of a custom hook you would make that wouldn't make use of other hooks.
  • We're returning two things back to the consumer of this custom hook: a list of breeds (including an empty list when it doesn't have anything in it) and an enumerated type of the status of the hook: unloaded, loading, or loaded. We won't be using the enum today but this is how I'd design it later if I wanted to throw up a nice loading graphic while breeds were being loaded.

Head over to SearchParam.js and put this in there.

import useBreedList from "./useBreedList";

// replace `const breeds = [];`
const [breeds] = useBreedList(animal);

That should be enough! Now you should have breeds being populated everything you change animal! (Do note we haven't implemented the submit button yet though.)

🏁 Click here to see the state of the project up until now: 06-custom-hooks