How should I make custom react hook if I need to send requests to many endpoints?

Viewed 1042

I need to send GET and POST requests to many endpoints.

For example, GET | POST Fruit List, GET | POST Weather List...

Not on one page, they are divided(Browser Route). ex) Fruit Page, Weather Page...

I'd like to know how to make a custom react hook in this situation.

I searched a lot, but all the writing was based on a single api endpoint.


Way 1

Create a reusable fetch function.

function useGetFetch(url){
    const response = await fetch(url, ...
    return response.json()
}

function usePostFetch(url){
    const response = await fetch(url, ...
    return response.json()
}

Way 2

Create function one for each.

function getFruitList(){
    const response = await fetch('/fruit', ...
}

function postFruitList(){
    const response = await fetch('/fruit', ...
}

function getWeatherList(){
    const response = await fetch('/weather', ...
}

function postWeatherList(){
    const response = await fetch('/weather', ...
}
1 Answers

I read about Kent's useAsync hook once and I think with your second approach will fit well. Here is the whole code of what goes into making a re-usable useAsync hook with star-wars API example :-

import "./styles.css";
import React, { useEffect } from "react";
export default function App() {
  const {
    run: starShipExec,
    status: starshipStatus,
    data: starshipData,
    error: startshipError
  } = useAsync({ status: "idle" });
  const {
    run: peopleExec,
    status: peopleStatus,
    data: peopleData,
    error: peopleError
  } = useAsync({ status: "idle" });

  useEffect(() => {
    starShipExec(getStarships());
    peopleExec(getPeople());
  }, [starShipExec, peopleExec]);

  return (
    <div className="App">
      Startships
      <ul>
        {starshipData?.results.map((res) => (
          <li>{res.name}</li>
        ))}
      </ul>
      People
      <ul>
        {peopleData?.results.map((res) => (
          <li>{res.name}</li>
        ))}
      </ul>
    </div>
  );
}

async function getStarships() {
  return await fetch("https://swapi.dev/api/starships");
}

async function getPeople() {
  return await fetch("https://swapi.dev/api/people");
}

function asyncReducer(_state, action) {
  switch (action.type) {
    case "pending": {
      return { status: "pending", data: null, error: null };
    }
    case "resolved": {
      return { status: "resolved", data: action.data, error: null };
    }
    case "rejected": {
      return { status: "rejected", data: null, error: action.error };
    }
    default: {
      throw new Error(`Unhandled action type: ${action.type}`);
    }
  }
}

function useSafeDispatch(dispatch) {
  const mountedRef = React.useRef(false);
  React.useLayoutEffect(() => {
    mountedRef.current = true;
    return () => (mountedRef.current = false);
  }, []);

  const safeDispatch = React.useCallback(
    (...args) => {
      mountedRef.current && dispatch(...args);
    },
    [dispatch]
  );
  return safeDispatch;
}

function useAsync({ status }) {
  const [state, unsafeDispatch] = React.useReducer(asyncReducer, {
    status: status,
    data: null,
    error: null
  });
  const dispatch = useSafeDispatch(unsafeDispatch);

  const run = React.useCallback(
    (promise) => {
      if (!promise) {
        return;
      }
      dispatch({ type: "pending" });
      promise
        .then((data) => data.json())
        .then((data) => {
          dispatch({ type: "resolved", data });
        })
        .catch((error) => {
          dispatch({ type: "rejected", error });
        });
    },
    [dispatch]
  );

  return { ...state, run };
}

The part I like is returning the run function into which we can pass the any promise and the rest the hook manages. It gives the user of the API more control of what to execute.

Here is a working codesandbox :-

Edit useAsync-star-wars

Related