【问题标题】:UseEffect being called multiple timesUseEffect 被多次调用
【发布时间】: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;

【问题讨论】:

    标签: javascript reactjs fetch fetch-api use-effect


    【解决方案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);....}
      猜你喜欢
      • 1970-01-01
      • 2020-01-05
      • 2020-03-03
      • 2021-11-26
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2022-06-12
      • 1970-01-01
      相关资源
      最近更新 更多