DEV Community

loading...
Cover image for Creating custom hook for fetching data in react

Creating custom hook for fetching data in react

Keyur Paralkar
Front-end developer👨‍💻; Book enthusiasts📖; Actively looking for frontend developer job opportunities.
・5 min read

Prerequisite to implement the customHooks

  • Knowlegde of functional components, and react hooks.
  • Libraries/Packages required: Redux, Axios.
  • Placeholder API such as: jsonplaceholder

Topics covered in this blogpost:

  1. Architectural pattern that is used for creating custom hook with axios.get().
  2. Implementation of custom hook.
  3. Usage in the actual component.
  4. More usecases for the custom hook like addition of adhoc redux actions, and multiple REST method implementation.

Architecture of our custom hook.

  • Every custom hook generally returns a state, it may or may not have a useEffect inside it. Let us name our custom hook as useFetch. According to the Rules of Hook every custom hook should have use as a keyword in front of it to recognize it as a hook.
  • useFetch is going to consist of the following blocks:
    • Parameters:
    • List of parameters such as URL, the method type, bodyand, headers.
    • State Block:
    • This will consists of all the local states i.e. useState
    • useEffect Block:
    • This will consists of the logic for axios call that we are going to make to the server.

Below diagram will provide a clearer view on how useFetch is designed:

useFetch UML

Implementing our custom hook: useFetch

  1. Let us first create a function which accepts a url as a parameter. We will also include the local state variables to this function.
const useFetchData = (url) => {
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);
};
Enter fullscreen mode Exit fullscreen mode

the function described above will consists of useStates as

  • isLoading for checking if the API has fetched the data or is still fetching the data,
  • apiData: If data is fetched successfully then the data is stored in apiData variable,
  • serverError: If there is any error in fetching the data from the API endpoint then we will store that error in serverError variable.
  1. We are going to add an useEffect react hook. This hook will consists of an axios.get(URL) call on the requested URL.
const useFetch = (url) => {
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    const fetchData = async () => {
      try {
        const resp = await axios.get(url);
        const data = await resp?.data;

        setApiData(data);
        setIsLoading(false);
      } catch (error) {
        setServerError(error);
        setIsLoading(false);
      }
    };

    fetchData();
  }, [url]);
};
Enter fullscreen mode Exit fullscreen mode

The axios call is enclosed in a async function named fetchedData. It consists of try...catch block. Once the data is awaited we store it in apiData using setApiData. If you have observed I have also set isLoading to true at the start of the useEffect. This is done intentionally because we want to show the loader when the API has initiated a call to the server. Once we get the response with 200 status we set the isLoading to false using setIsLoading.

If by some chance there is any error, we set the serverError state to error along with isLoading state to false.

  1. Finally we are going to return all the local state variables as an object.
const useFetch = (url) => {
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    const fetchData = async () => {
      try {
        const resp = await axios.get(url);
        const data = await resp?.data;

        setApiData(data);
        setIsLoading(false);
      } catch (error) {
        setServerError(error);
        setIsLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { isLoading, apiData, serverError };
};
Enter fullscreen mode Exit fullscreen mode

Usage in the actual component

Let us look at an example were we can use our custom hook useFetch. Below is the code of the index.js file.

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import useFetch from "./useFetch";

const App = () => {
  const { isLoading, serverError, apiData } = useFetch(
    "https://jsonplaceholder.typicode.com/posts/1"
  );
  return (
    <div>
      <h2>API data</h2>
      {isLoading && <span>Loading.....</span>}
      {!isLoading && serverError ? (
        <span>Error in fetching data ...</span>
      ) : (
        <span>{JSON.stringify(apiData)}</span>
      )}
    </div>
  );
};

const rootElement = document.getElementById("root");

ReactDOM.render(
  <StrictMode>
    <App />
  </StrictMode>,
  rootElement
);
Enter fullscreen mode Exit fullscreen mode

Now just like any React hook we can directly use our custom hook to fetch the data. As you can see, isLoading and serverError can be used for conditional rendering of the component for displaying nice error messages.

Additional use cases

We can have additional use cases such as Adding adhoc redux actions, Generalizing the API methods etc. Below is the brief overview of the two use cases.

  1. Adding Adhoc redux actions: You can also integrate redux to your application and add an API response to your global state. Modification to the useFetch would look like this:
const useFetchData = (url) => {
  const dispatch = useDispatch();
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    const fetchData = async () => {
      try {
        dispatch(fetchApiData());
        const resp = await axios.get(url);
        const data = await resp?.data;

        dispatch(fetchApiSuccess(data));
        setApiData(data);
        setIsLoading(false);
      } catch (error) {
        setServerError(error);
        dispatch(fetchApiFailure());
        setIsLoading(false);
      }
    };

    fetchData();
  }, [dispatch, url]);

  return { isLoading, apiData, serverError };
};
Enter fullscreen mode Exit fullscreen mode

fetchApiData, fetchApiSuccess, and fetchApiFailure are thunks which call the specific redux actions along with storing the data onto the redux global state.

  1. Generalizing API methods Our useFetch is currently performing only GET request. Ideal scenario is to have our useFetch to perform all types of request such as POST, PUT etc. Following code will make a generalized axios call.
const useFetch = (method, url, body) => {
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    const fetchData = async () => {
      try {
        const resp = await axios({
          method: method,
          url: url,
          data: body
        });
        const data = await resp?.data;

        setApiData(data);
        setIsLoading(false);
      } catch (error) {
        setServerError(error);
        setIsLoading(false);
      }
    };

    fetchData();
  }, [url, method, body]);

  return { isLoading, apiData, serverError };
};
Enter fullscreen mode Exit fullscreen mode

Usage will be the same as that of index.js. Only thing that is changing is the function definition:

const { isLoading, serverError, apiData } = useFetch(
    "GET",
    "https://jsonplaceholder.typicode.com/posts/1",
    {}
  );
Enter fullscreen mode Exit fullscreen mode

These are some of the use cases which I think can most commonly used. You can you the above custom hook to fetch the data and enhance it as per our needs.

You can find the code used in this blogpost at the following sandbox URL:
https://codesandbox.io/s/react-custom-hook-sample-dcuf4

Feel free to reach out to me @

Linkedin Badge

Mail Badge

Twitter Badge

Github Badge

Discussion (0)