DEV Community

Cover image for React: `useFetchData` Custom Hook
Vishal Gupta
Vishal Gupta

Posted on

React: `useFetchData` Custom Hook

If you observe common state logic in many components make a custom hook

React hooks are very useful when you want to take out common logic out of your components.

In our live projects we make some components which makes API calls, parse data and display it to the user.

There are lot of state transitions happening behind this.

* Initial State
* Loading
* Data Received

These 3 are the common state transitions for each and every component, which means we can create a custom hook out of it.

Now, let's make a custom hook.

Custom Hook
import { useEffect, useReducer, useCallback } from 'react';


const INITIAL_STATE = {
  data: undefined,
  isLoading: false,
  hasError: false,
};

/**
 * Hook to fetch data and handling loading and error state.
 * @param {Promise} fetchData promise to fetch data. [Dependancy]
 * @param {Array} params parameters to pass to fetch data. [Dependancy]
 */
const useFetchData = (fetchData, params = EMPTY_ARRAY, initialState = INITIAL_STATE) => {
  const [state, dispatch] = useReducer(REDUCER, initialState);

  const usefetchDataEffect = useCallback(() => fetchDataEffect(dispatch, fetchData, params), [fetchData, ...params]);

  useEffect(usefetchDataEffect, [usefetchDataEffect]);

  return [state, { fetchData: usefetchDataEffect }];
};

export default useFetchData;

Helper Functions

const setLoading = dispatch => dispatch({ type: 'SET_LOADING' });

const setError = dispatch => dispatch({ type: 'SET_ERROR' });

const setData = (dispatch, data) => dispatch({
  type: 'SET_DATA',
  payload: { data },
});

/**
 * Fetches and saves Data.
 * Handles Error.
 * @param {Promise} fetchData promise which returns data
 * @param {Array} params parameters to be passed to fetchData promise
 * @param {Function} dispatch function to update state
 */
const fetchDataEffect = (dispatch, fetchData, params) => {
  setLoading(dispatch);
  fetchData(...params)
    .then((data) => {
      setData(dispatch, data);
    })
    .catch(() => {
      setError(dispatch);
    });
}

Usage

const [{ isLoading, hasError, data: componentData }, { fetchData }] = useFetchData(fetchComponentData, [dependency]);

NOTE

Try to parse the API response in fetchComponentData function so the component receives parsed data to display.

For simplicity, I have not included definition of reducers.

Top comments (0)