【问题标题】:I Got Stuck in infinite loop in react.js. How to resolve this?我在 react.js 中陷入无限循环。如何解决这个问题?
【发布时间】:2021-07-03 20:58:14
【问题描述】:

我在 react.js 中陷入了无限循环。如何解决这个问题?

useEffect(() => {
    fetch("https://react-http-d55a9-default-rtdb.firebaseio.com/todo.json")
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        console.log(data);
        setUsersList((prev) => [...prev]);           //cause of infinite loop
      });
  }, [usersList]);

【问题讨论】:

  • 为什么要在 usersList 更改时运行此效果?
  • 因为要确保每当 userList 更新时,应该从数据库中获取数据并呈现在屏幕上。
  • 为什么在 userList 更改后必须从 db 中获取,而您并没有真正在 HTTP 请求的正文中或作为参数传递 userList?无论如何,API 将返回相同的数据。如果您在通过其他 api 更改时将 userList 持久化到 db,那么您不应该将其保留为效果。相反,您可以将其作为一个函数并从用于将 userList 持久保存在数据库中的 POST 或 PUT api 的 then 部分调用该函数。

标签: reactjs dependencies use-effect


【解决方案1】:

你有一个无限循环,因为你的 useEffect 依赖数组上有 usersList ,同时你在你的 useEffect 函数中更新这个变量。因此,您的useEffect 在组件安装时运行,这会更新您的usersList,这会使useEffect 再次运行,这再次更新您的usersList,使其再次运行等等...
要解决此问题,请从依赖项数组中删除 usersList,并改为使用空数组:[]。如果您这样做,您的useEffect 将在您的组件挂载时运行一次。

【讨论】:

  • 但是如果我将使用空的依赖数组,那么我的 useEffect 将只在开始时运行一次,当我刷新页面时,但在那之后这个 useEffect 将如何被调用。?是否有任何其他方法可以通过确保在 usersList 更改时调用 useEffect 来解决..
  • usersList 改变时调用这个useEffect,你不能在其中更新usersList,如果你这样做,你会得到一个无限循环,就像我上面解释的那样。您能否更深入地了解为什么您希望每次更新 usersLists 时都运行它,以及您如何在此 useEffect 之外更新它?
【解决方案2】:

传递给useEffect 的依赖列表决定了效果应该何时再次运行。无限循环正在发生,因为此效果会导致 usersList 发生变化,从而触发效果再次运行。

由于此效果不使用任何其他变量,因此它的依赖列表中不需要任何内容​​:

useEffect(() => {
  fetch(...)
  // ...
}, []); // leave this empty, so the effect only runs when the component mounts

如果你的 URL 依赖于一个 prop 或其他东西,那么你希望它在依赖列表中:

useEffect(() => {
  fetch(`https://example.com/todo/${props.id}`)
    .then(...)

  // Since the URL depends on the id prop, the effect should re-run if it changes
}, [props.id]);

【讨论】:

  • 但是有一个问题,如果我要取一个空的依赖数组,那么我的 useEffect 将在我刷新页面的开始时只运行一次,但之后这个 useEffect 将如何叫做。?是否有任何其他方法可以通过确保在用户列表更改时调用 useEffect 来解决
  • 效果负责设置用户列表,这就是它最后调用setUserList的原因。当usersList 更改时运行效果没有意义,因为这就是导致无限循环的原因。 (我假设 usersListsetUsersList 是从 useState 返回的一对,如果不是这样,那么您需要显示整个组件的来源,以便我们了解您应该做什么)。
  • 两者都是配对的。你这么说是什么意思 - 当 usersList 更改时运行效果没有意义。你的意思是如果这个 usersList 更新了,那么这个 useEffect 也会被再次调用。如果这就是您要说的,那么我想确认一件事,如果我们将依赖项设为空,那么 useEffect 只调用一次是否正确?
  • 是的,一个空的依赖数组意味着效果只会在组件挂载时运行一次。我建议阅读useEffect documentation,它会比我在这里做的更好地解释一切是如何工作的。
  • 好的,所以现在出现了两个主要问题,如果我在这里取一个空的依赖数组,那么这个 useEffect 将不会被再次调用?但是,如果我想在每次用户列表更改时执行这个 useEffect,那么我该怎么做呢?如果我在这里取一个空的依赖数组,那么这将不会再次获取数据?
【解决方案3】:

根据提出的问题,您希望 userList 每次更新时都被监视。我们可以做的是再定义一个状态变量,正如代码中提到的那样,isFetched 或者如果你正在使用 redux,你可以把它放在那里,因为如果我们只是观察 userList 变量,那么它会陷入无限循环,因为设置 userList 是发生在 useEffect 本身。在 isFetched 的帮助下,我们可以管理何时调用 api 以及当标志为 false 时调用 api。

现在我在代码中添加了一个状态变量作为 setCount,因为我不知道你想调用多少次你的 api。所以你可以把你的条件放在那里,当你的条件满足时停止通话。

function App() {
  const [userList, setUserList] = useState([]);
  const [isFetched, setIsFetched] = useState(false);
  const [, setCount] = useState(3);

  const callApiPending = useCallback(()=>{
    fetch("https://react-http-d55a9-default-rtdb.firebaseio.com/todo.json")
    .then((response) => response.json())
    .then((json) => {
      setUserList((prev) => [...prev, ...json]);
      setCount((cnt) => {
        if(cnt - 1 === 0){
          setIsFetched(true);
        }
        return cnt - 1;
      });
      
    });
  }, []);

  useEffect(() => {
    if (!isFetched) {
      callApiPending();
    }
  }, [isFetched, userList, callApiPending]);

  return <div>Executing....</div>;
}

【讨论】:

    【解决方案4】:

    如果 usersList 发生变化,您会运行 fetch。即使 userList 的内容与以前的内容相同,javascript 也会解释为它发生了变化。试试这个。

    [1,2,3] == [1,2,3]
    

    可能返回 false。您可以使用用于检查是否获取数据的标志而不是使用数组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 2021-11-21
      • 2022-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多