useEffect 依赖导致无限循环

useEffect dependency causes infinite loop

我创建了一个我在 App.js

中使用的自定义挂钩

自定义钩子(相关函数为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,
  };
}

然后在我的 App.js:

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

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

我的 IDE 建议添加 fetchTasks 函数作为对 useEffect 的依赖。但是一旦我添加它,就会创建一个无限循环。如果我从代码中所示的依赖项中省略它,它将按预期工作,但我知道这是一种不好的做法。那我该怎么办?

而不是 return fetchTasks 函数 return 这个 useCallback fetchTasksCallback 函数来自 useFetch 钩子,它只创建了一个 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]);

问题是这个fetchTasks每次创建一个新的实例这样依赖列表感觉有变化并重复useEffect导致无限循环问题的代码块

因为每次你打电话useFetch()fetchTasks 函数将是 re-created。这会导致引用在每次渲染时发生变化,然后 useEffect() 将检测到依赖项 fetchTasks 是 re-created 并再次执行它,并进行无限循环。 因此,您可以利用 useCallback() 来记忆您的 fetchTasks() 函数,这样引用将保持不变。

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]);