useEffect 导致无限循环或出错

时间:2021-01-27 02:56:18

标签: reactjs react-hooks use-effect

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

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

当我使用 isLoading 时,它可以工作,但随后我收到错误“localhost/:1 Unchecked runtime.lastError:消息端口在收到响应之前关闭。”和“localhost/:1 错误处理响应:TypeError:无法读取未定义的属性‘级别’”

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”

我做错了什么?

2 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:0)

无限循环是由 setData(result)[data] 组合使用引起的:

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

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

使用未包含在依赖项数组中的外部(useEffect)变量可能意味着该变量的值会发生变化并且不会重新触发 useEffect 或该值可能不是预期值。

以下是如何修复原始代码段的示例:

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;