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?

3

2 Answers 2

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]);
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.