【问题标题】:useEffect causing infinite loop or getting errorsuseEffect 导致无限循环或出现错误
【发布时间】:2021-04-30 21:10:31
【问题描述】:

我正在尝试学习 React 钩子。我正在创建一个利用 NY Times api 的简单新闻应用程序。

当我将依赖项留空时,不会加载任何内容,当我使用数据作为依赖项时,它会进入无限循环。

当我使用 isLoading 时,它可以工作,但随后我收到错误“localhost/:1 Unchecked runtime.lastError:消息端口在收到响应之前关闭。”和“localhost/:1 错误处理响应:TypeError: Cannot read property 'level' of undefined”

main.js

import React, { useEffect, useState } from "react";
import { nyTimesApi } from "../services/Api";
const Main = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState([]);

  const fetchData = async () => {
    const result = await nyTimesApi();
    setData(result);
    setIsLoading(false);
    console.log(data.results);
  };
 
  useEffect(() => {
    fetchData();
  }, [isLoading]);
  return <div className="main">work</div>;
};
export default Main;

当我使用 isLoading 时,我还在终端中收到警告说“React Hook useEffect 缺少依赖项:'fetchData'。要么包含它,要么删除依赖数组 react-hooks/exhaustive-deps”

我做错了什么?

【问题讨论】:

  • 您的代码完全没问题。能否分享更多源代码或在 CodeSanbox 中重现?
  • 你不能在下一行console.log状态改变,状态改变是异步的。在函数主体中记录状态以查看最新的状态值。你的效果实际上可以只是useEffect(fetchData, []);,所以它在挂载上运行一次
  • 如果您希望函数仅在初始加载时运行,则依赖项数组应该为空。我很确定你可以忽略依赖数组中需要“fetchData”的警告。

标签: reactjs react-hooks use-effect


【解决方案1】:

useEffect 的第二个参数是一个变量数组,每次更改时都会触发 useEffect 中的函数。

您将 [isLoading] 作为 useEffect 的第二个参数,并在 fetchData() 中更新 this 的值。这将导致 useEffect 触发器一次又一次地发生。

如果你只想调用一次useEffect(类似于基于类的组件中的ComponentDidMount),那么你需要指定一个空数组作为第二个参数。

useEffect(() => {
  fetchData();
}, []);

【讨论】:

  • 虽然对于对象、数组、函数等的参考检查来说是准确的,但循环解释对于 isLoading 是不准确的,它是一个布尔值。布尔值将是一个值比较,在这种情况下应该只会导致一次额外的意外执行 useEffect。该问题指出,无限循环是由使用 data(一个对象)代替依赖数组中的 isLoading 引起的。
【解决方案2】:

死循环是由于使用setData(result)[data]的组合造成的:

  1. 组件挂载,useEffect 运行。
  2. setData(result) 将异步更新 data 值并触发重新渲染。
  3. 在重新渲染期间,useEffect 将再次运行,因为data 将无法成功完成参考比较。
  4. 重复 2 到 3。

警告“React Hook useEffect has a missing dependency”在一定程度上是不言自明的。

使用未包含在依赖数组中的外部(useEffect)变量可能意味着变量的值发生更改,useEffect 将不会被重新触发,或者该值可能不是期望值。

以下是如何修复原始 sn-p 的示例:

import React, { useEffect, useState } from "react";
import { nyTimesApi } from "../services/Api";

const Main = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState([]);
 
  useEffect(() => {
    // Create function inside useEffect so that the function is only
    // created everytime the useEffect runs and not every render.
    const fetchData = async () => {
        const result = await nyTimesApi();
        setData(result);
        setIsLoading(false);

        // setData will update state asynchronously.
        // Log the value stored instead.
        console.log(result.results);
    };

    //Run data fetching function.
    fetchData();

  }, 
  // Both of these are set functions created by useState and will
  // not change for the life of the component, but adding them to
  // the dependency array will make your linter happy.

  // Do not need to check isLoading as it is always true on component
  // mount.
  [setData, setIsLoading]);

  return <div className="main">work</div>;
};

export default Main;

【讨论】:

    猜你喜欢
    • 2020-08-27
    • 2020-11-16
    • 2021-11-01
    • 2021-09-27
    • 2020-11-14
    相关资源
    最近更新 更多