React UseEffect 被调用两次。

16

在FC组件中,我有以下代码:

我只想在组件挂载时调用API并记录信息

控制台日志会对空数组调用两次,并对设置相同值的情况调用两次。

import * as React from"react";
import Header from"../components/Header";
import Search from"../components/Search";
import Categories from"../components/Categories";
import Carousel from"../components/Carousel";
import CarouselItem from"../components/CarouselItem";
import Footer from"../components/Footer";
import "../assets/styles/App.scss";

const App = () => {
  const [videos, setVideos] = React.useState([]);

  React.useEffect(() => {
    fetch("http://localhost:3000/initalState")
      .then((response) => response.json())
      .then((data) => setVideos(data));
  }, []);

  console.log(videos);
  return (
    <div className="App">
      <Header></Header>
      <Search></Search>

      <Categories title="Mi Lista">
        <Carousel>
          <CarouselItem />
          <CarouselItem />
          <CarouselItem />
          <CarouselItem />
          <CarouselItem />
        </Carousel>
      </Categories>

      <Categories title="Tendencias">
        <Carousel>
          <CarouselItem />
          <CarouselItem />
        </Carousel>
      </Categories>

      <Categories title="Originales de Platzi Video">
        <Carousel>
          <CarouselItem />
        </Carousel>
      </Categories>

      <Footer />
    </div>
  );
};

export default App;

然而,我得到了以下的输出:

输入图像描述

在 useEffect 中进行调用:

  React.useEffect(() => {
    fetch("http://localhost:3000/initalState")
      .then((response) => response.json())
      .then((data) => {
        setVideos(data);
        console.log(videos);
      });
  }, []);
返回一个空数组。

你正在外部打印它。尝试在useEffect回调函数中打印它。 - Amit Joki
还要验证只有一个<App/>组件被挂载。可能已经是这样了,但首先清除明显的问题会有所帮助。 - charlietfl
是的,只有一个应用正在挂载。 - Juan Bonoso
@AmitJoki,我刚刚更新了这篇文章,用于在useEffect回调中打印,但是它给了我一个空数组。 - Juan Bonoso
2个回答

48

我刚意识到我在严格模式下渲染了应用程序,这会导致某些事件被触发两次:

严格模式无法自动检测副作用,但可以通过使它们更加确定性来帮助您检测副作用。这是通过有意双重调用以下函数来完成的:

类组件构造函数、render 和 shouldComponentUpdate 方法 类组件静态 getDerivedStateFromProps 方法 Function component 函数内部 状态更新函数(传递给 setState 的第一个参数) 传递给 useState、useMemo 或 useReducer 的函数

原本代码如下:

ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById("root")
    );

现在

ReactDOM.render(<App />, document.getElementById("root"));

问题已解决。


你帮了我很大的忙。 - Igor William
1
移除 StrictMode 应该是最后的选择。如需更详细的答案,您可以访问 https://dev59.com/OlEG5IYBdhLWcg3wJlLh。 - Youssouf Oumar

2
您可以使用 UseCallback 来避免额外的运行。以下是示例代码。
注意:如果您在 use effect 中直接调用 setTimeout/server calls 而没有包装在 useCallback 中,它将被调用两次。
const App = () => {
  const [myData, setMyData] = React.useState([]);


  const getData = useCallback ( () => { 
    setTimeout(() => {
      console.log("Inside setTimeOut");
      setMyData("HEllo");
    }, 2000);

  }, [] )

  useEffect(() => {
    getData()
     console.log("Inside UseEffect",myData);
   },[getData]);


  return (
    <div className="App">
     Inside my App {myData}
    </div>
  );
}

export default App;

输出:

Inside UseEffect  [printed just once]

我明白了。我应该在useEffect钩子之外使用console.logs吗?与useEffect、return和getData处于同一级别? - Juan Bonoso
不包装 useEffect 并添加依赖项,它将会有更多的循环,因为 getdata 将有新数据再次渲染并重新打印日志。通过包装 useEffect 并添加依赖,只有在依赖项改变时它才会运行。 - upog
我已经删除了所有myData的依赖项,并保留了其他console打印。它会打印两次“Inside UseEffect”。 - Shan Biswas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接