reactjsreact-hooks

React Custom Fetch Hook, force Refetch


I am trying to generate a CustomHook that receives a function in charge of performing the query. The CustomHook should perform the query and return it.

ISSUE: When I change the parameter, it is not reflected in the component on the first click. Try putting the object with the parameters in the useEffect and it loops forever

Expected: Change a parameter from the front and automatically through useEffect or by executing a function, make the query again with the new parameter stored in a hook.

(The parameter must be an array, it is a function that I want to reuse)

Actual https://codesandbox.io/s/usefetchwithrefetch-iw4r33?file=/src/App.js:739-745

App.js

import { useState } from "react";
import "./styles.css";
import useFetch from "./useFetch";

export default function App() {
  const url = "https://api.agify.io/";

  const fetcher = (urlToFetch) => {
    return fetch(urlToFetch);
  };

  const getYearByName = async (name) => {
    const response = await fetcher(url + `?name=${name}`, {
      method: "GET"
    });
    return await response.json();
  };

  const [name, setName] = useState("Nahuel");

  const { data, isLoading, getData } = useFetch([name], getYearByName);

  return (
    <div className="App">
      <button onClick={() => {
        setName("Camila") 
        getData()}}>
          Change name
      </button>
      {isLoading ? (
        <div> cargando </div>
      ) : (
        <>
          <h1>{data.name}</h1>
          <h2>{data.age}</h2>
        </>
      )}
    </div>
  );
}

useFetch.js

import { useState, useEffect } from "react";

const useFetch = (obj, functionTest) => {
  const [data, setData] = useState([]);
  const [error, setError] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  const getData = async () => {
    setIsLoading(true);
    const response = await functionTest(...obj);
    setData(response);
    setIsLoading(false);
    setError(false);
  };
  useEffect(() => {
    getData();
  }, []);

  return {
    data,
    error,
    isLoading,
    getData
  };
};

export default useFetch;

Solution

  • What you did is good.The infinite loop happened because you create a new value [name] on each call!

    import { useState, useEffect } from "react";
    
    const useFetch = (obj, functionTest) => {
      const [data, setData] = useState([]);
      const [error, setError] = useState(false);
      const [isLoading, setIsLoading] = useState(true);
    
      const getData = async () => {
        setIsLoading(true);
        const response = await functionTest(...obj);
        setData(response);
        setIsLoading(false);
        setError(false);
      };
      useEffect(() => {
        getData();
      }, [obj]);
    
      return {
        data,
        error,
        isLoading,
        getData
      };
    };
    
    export default useFetch;
    

    Additionally, obj should be memoized on the caller side

    const obj = useMemo(() => ([name]), [name]);