1

I created a custom hook which I use in App.js

The custom hook (relevant function is fetchTasks):

export default function useFetch() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [tasks, setTasks] = useState([]);

  const fetchTasks = async (url) => {
    setLoading(true);
    setError(null);
    try {
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error("falied!");
      }
      const data = await response.json();
      const loadedTasks = [];
      for (const taskKey in data) {
        loadedTasks.push({ id: taskKey, text: data[taskKey].text });
      }

      setTasks(loadedTasks);
    } catch (err) {
      console.log(err.message);
    }
    setLoading(false);
  };

  return {
    loading,
    setLoading,
    error,
    setError,
    fetchTasks,
    tasks,
  };
}

Then in my App.js:

function App() {
  const { loading, setLoading, error, setError, fetchTasks, tasks } =
    useFetch();

  useEffect(() => {
    console.log("fetching");
    fetchTasks(
      "https://.....firebaseio.com/tasks.json"
    );
  }, []);

My IDE suggests adding the fetchTasks function as a dependency to useEffect. But once I add it, an infinite loop is created. If I omit it from the dependencies as shown in my code, it will work as expected, but I know this is a bad practice. What should I do then?

sir-haver
  • 3,096
  • 7
  • 41
  • 85
  • Does this answer your question? [How to fix missing dependency warning when using useEffect React Hook](https://stackoverflow.com/questions/55840294/how-to-fix-missing-dependency-warning-when-using-useeffect-react-hook) – Rafael Tavares Apr 05 '22 at 17:23
  • 1
    The reason it creates an infinite loop is that every time you call `useFetch` you create a new function called `fetchTasks`, so the reference changes at every render. Instead you can leverage [the `useCallback` hook](https://reactjs.org/docs/hooks-reference.html#usecallback) [as referenced in this answer](https://stackoverflow.com/a/60327893/6831341) to memoize your `fetchTasks` function so the reference remains unchanged at each usage of your `useFetch` hook. – Alexander Nied Apr 05 '22 at 17:26
  • I understand now thanks for your answers – sir-haver Apr 05 '22 at 18:03

2 Answers2

1

Because that every time you call useFetch(). fetchTasks function will be re-created. That cause the reference to change at every render then useEffect() will detected that dependency fetchTasks is re-created and execute it again, and make the infinite loop. So you can leverage useCallback() to memoize your fetchTasks() function so the reference will remains unchanged.

import { useCallback } from 'react'

export default function useFetch() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [tasks, setTasks] = useState([]);

  const fetchTasks = useCallback(
    async (url) => {
    setLoading(true);
    setError(null);
    try {
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error("falied!");
      }
      const data = await response.json();
      const loadedTasks = [];
      for (const taskKey in data) {
        loadedTasks.push({ id: taskKey, text: data[taskKey].text });
      }

      setTasks(loadedTasks);
    } catch (err) {
      console.log(err.message);
    }
    setLoading(false);
  };,[])

  return {
    loading,
    setLoading,
    error,
    setError,
    fetchTasks,
    tasks,
  };
}
function App() {
  const { loading, setLoading, error, setError, fetchTasks, tasks } =
    useFetch();

  useEffect(() => {
    console.log("fetching");
    fetchTasks(
      "https://.....firebaseio.com/tasks.json"
    );
  }, [fetchTasks]);
0

instead of return fetchTasks function return this useCallback fetchTasksCallback function from useFetch hook which created only one instance of fetchTasksCallback.

const fetchTasksCallback = useCallback(
  (url) => {
    fetchTasks(url);
  },
  [],
);

function App() {
  const { loading, setLoading, error, setError, fetchTasksCallback, tasks } =
    useFetch();

  useEffect(() => {
    console.log("fetching");
    fetchTasksCallback(
      "https://.....firebaseio.com/tasks.json"
    );
  }, [fetchTasksCallback]);

the problem is this fetchTasks every time create a new instance that way dependency list feels that there is a change and repeats the useEffect code block which causes the infinite loop problem

Abbas Hussain
  • 1,277
  • 6
  • 14