UseEffect 被多次调用

Posted

技术标签:

【中文标题】UseEffect 被多次调用【英文标题】:UseEffect being called multiple times 【发布时间】:2020-10-19 04:46:18 【问题描述】:

我以为useEffect 只在渲染后调用一次,但它被执行了多次,而且不是按照我预期的顺序。

我希望它在提取过程中发送消息“数据加载”,然后在提取完成后,呈现标题字段并警告“..Done...”一次,这应该是结束。

我在两个点​​添加了 ALERT 和控制台日志以确定流程,并且警报和控制台日志都以不同的顺序出现不止一次。您能否运行此代码并查看行为。我将第二个参数数组保留为 null 以使其仅运行一次但没有帮助。

请澄清反应 RENDER 是否意味着在屏幕上显示? LOAD表示什么阶段?什么时候展示?

代码如下:

import React,  useEffect, useState  from "react";
//import "./App.css";

function DemoFetchZ() 
  let data =  title: "Waiting for Data" ;
  const [todo, setTodo] = useState(data);
  const [isData, setData] = useState(false);
  const [isFetching, setFetching] = useState(false);

  useEffect(() =>  // called after the first render
    async function fetchData() 
      setFetching(true);
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/todos/1"
      );
      console.log("response = ", response);
      let data = await response.json();
      setTodo(data); //updt state
        setFetching(false);
        setData(true)
      console.log("Data = ", data);
    
    fetchData();
  , []); //[isData] null value will execute once only?

  if (isFetching) 
      console.log("data loading ......")
      alert ("data loading")
      return (<div>...Data Loading.....</div>);
  

  return (
    <div>
           - Fetch
          <br /> alert("..DONE...")
      <span>Title: todo.title</span>
    </div>
  );


export default DemoFetchZ;

【问题讨论】:

【参考方案1】:

您的useEffect 在每个渲染周期只执行一次,但您的useEffect 中有几个状态更新会导致重新渲染。因此,您会收到很多警报。

See a demo of your code 并查看 console.logs 以及 cmets

还要注意useEffect 会

当你提供空数组依赖时,你的 useEffect 执行一次 当您将某个值作为依赖项(例如:[name])时,您的 useEffect 在名称状态/属性更改时执行 如果不传递依赖数组,则每次重新渲染时都会执行 useEffect。

Read here on re-render

【讨论】:

【参考方案2】:

像这样重构它。

import React,  useEffect, useState  from "react";
//import "./App.css";

const DemoFetchZ = () => 
  const [todo, setTodo] = useState();
  const [loading, setLoading] = useState(false);

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

  const fetchData = () => 
    setLoading(true);
    fetch("https://jsonplaceholder.typicode.com/todos/1")
      .then((response) => response.json())
      .then((data) => 
        setTodo(data);
        setLoading(false);
      )
      .catch((error) => 
        console.log(error);
        setLoading(false);
      );
  ;

  return (
    <>
      loading ? (
        <div>...Data Loading.....</div>
      ) : (
        <div>
          - Fetch
          <br />
          <span>Title: todo ? todo.title : "no Title Found"</span>
        </div>
      )
    </>
  );
;

export default DemoFetchZ;

【讨论】:

看起来好多了。我要试试。一个问题。为什么不将函数 fetchData 作为异步等待? const fetchData = () => setLoading(true); fetch("jsonplaceholder.typicode.com/todos/1") .then((response) => response.json()) .. .then 就像 async-await 一样工作,对 DX 来说更好。在上面的代码中,您告诉编译器在响应为 200 时仅设置数据,否则设置错误。但在您的代码中,您不处理错误。 @SerajVahdati 我正在学习 react.js 并且遇到了类似的问题。我从这个答案中了解到,todoloading 等状态对象如果作为依赖项传递,则不应在useEffect 方法内更新,否则useEffect 将被一次又一次地调用。就我而言,我从 API 获取数据并将其显示在同一页面上。通过应用此答案中提到的代码修复,我有一个警告:“React Hook useEffect 缺少依赖项:'fetchData'。要么包含它,要么删除依赖项数组。” @Junaid 这只是一个警告,并不重要。这段代码没有任何问题。但如果你想修复它。您可以在useEffect 中声明您的fetchData 函数并在声明后立即调用它。它必须为你解决 一个小改进。如果您最终将依赖项放在useEffect 中,请检查您是否已经在 fetchData() 中拥有数据并返回而不是再次调用服务器,这将阻止您在不需要时向服务器发送相同的请求。 fetchData() if ( data ) return; setLoading(true);....

以上是关于UseEffect 被多次调用的主要内容,如果未能解决你的问题,请参考以下文章

ReactJs/NextJs useEffect() 总是被调用两次

ReactJS useEffect Hook - 在组件中多次使用?

useEffect和useLayoutEffect

在 useEffect() 中未调用函数

在useEffect中多次设置状态仅设置最后一个

如何处理由于从 Firestore DB 检索而多次运行的 UseEffect?