React Hooks:即使使用空数组作为参数,useEffect() 也会被调用两次

J.K*_*.Ko 27 reactjs react-hooks


我是 reactJS 的新手,正在编写代码,以便在从 DB 加载数据之前,它会显示加载消息,然后在加载后,使用加载的数据渲染组件。为此,我同时使用了 useState 钩子和 useEffect 钩子。这是代码:

问题是,当我检查 console.log 时,useEffect 被触发了两次。因此,代码两次查询相同的数据,这是应该避免的。

下面是我写的代码:

import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'

const indexarray=[]; //The array to which the fetched data will be pushed

function Home() {
   const [isLoading,setLoad]=useState(true);
   useEffect(()=>{
      /*
      Query logic to query from DB and push to indexarray
      */
          setLoad(false);  // To indicate that the loading is complete
    })
   },[]);
   if (isLoading===true){
       console.log("Loading");
       return <div>This is loading...</div>
   }
   else {
       console.log("Loaded!"); //This is actually logged twice.
       return (
          <div>
             <div className="posts_preview_columns">
             {indexarray.map(indexarray=>
             <Postspreview
                username={indexarray.username}
                idThumbnail={indexarray.profile_thumbnail}
                nickname={indexarray.nickname}
                postThumbnail={indexarray.photolink}
             />
             )}
            </div>
         </div>  
         );
    }
}

export default Home;
Run Code Online (Sandbox Code Playgroud)

有人可以帮助我理解为什么它被调用两次,以及如何正确修复代码?非常感谢!

小智 302

从 index.js 中删除 <React.StrictMode> 此代码将是

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

root.render(
    <App />
);
Run Code Online (Sandbox Code Playgroud)

React StrictMode 在开发服务器上渲染组件两次

  • @YashPokar根据我的理解,React StrictMode 渲染组件的次数如上所述。为什么?要识别具有不安全生命周期的组件,您可以在此处看到 -&gt; https://reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles 。换句话说,为了确保组件能够弹性安装和卸载,如果您使用 useEffect,最好的解决方案是编写清理函数。如果 useEffect 中确实有一些繁重的东西,也许可以将它们从 useEffect 中一起移出。 (7认同)
  • 天哪,StrictMode 就是罪魁祸首。是的,如果您也能对此提供更多解释,那就太好了 (2认同)
  • 删除 StrictMode 是最后要做的事情。它的存在是有目的的。请参阅/sf/ask/5056672281/ Called-two-times-on-mount。 (2认同)

rm_*_*rm_ 65

您很可能在启用了严格模式的开发环境中检查问题。\n要验证情况是否如此,请搜索 <React.StrictMode> 标记并将其删除,或构建生产环境。双重渲染问题应该消失了。\n来自 React 官方文档

\n
\n

严格模式可以\xe2\x80\x99t自动为您检测副作用,但它可以通过使副作用更具确定性来帮助您发现它们。这是通过有意两次调用以下函数来完成的:

\n
    \n
  • 传递给useState、 useMemo 或 useReducer的函数
  • \n
  • [...]
  • \n
\n
\n

严格模式 - Reactjs 文档

\n

类似的问题在这里我的 React 组件由于严格模式而渲染两次\n

\n


Tai*_*aig 43

正如其他人已经指出的那样,这种情况的发生很可能是由于React 18.0 中引入的严格模式功能所致。

我写了一篇博文,解释了为什么会发生这种情况以及您可以采取哪些措施来解决它。

但如果您只想查看代码,请看这里:

const initialized = useRef(false)

useEffect(() => {
  if (!initialized.current) {
    initialized.current = true

    // My actual effect logic...
    ...
  }
}, [])
Run Code Online (Sandbox Code Playgroud)

或者作为可重复使用的钩子:

import type { EffectCallback } from "react"
import { useEffect, useRef } from "react"

export function useOnMountUnsafe(effect: EffectCallback) {
  const initialized = useRef(false)

  useEffect(() => {
    if (!initialized.current) {
      initialized.current = true
      effect()
    }
  }, [])
}
Run Code Online (Sandbox Code Playgroud)

请记住,只有在绝对必要的情况下才应该采用此解决方案!

  • 为什么要用它作为最后的手段?..使用 useRef 作为哨兵标志是否有问题? (2认同)

Joe*_*oyd 39

把 console.log 放在 useEffect 里面

可能您还有其他副作用导致组件重新渲染,但 useEffect 本身只会被调用一次。您可以使用以下代码确定地看到这一点。

useEffect(()=>{
      /*
      Query logic
      */
      console.log('i fire once');
},[]);
Run Code Online (Sandbox Code Playgroud)

如果日志“我触发一次”被多次触发,则意味着您的问题是两件事之一。

此组件在您的页面中出现不止一次

这个应该很明显,你的组件在页面中出现了几次,每个都会挂载并运行 useEffect

树更高的东西正在卸载和重新安装

组件被强制卸载并在其初始渲染时重新安装。这可能类似于发生在树更高处的“关键”变化。您需要使用此 useEffect 提升每个级别,直到它仅呈现一次。那么您应该能够找到原因或重新安装。

  • 伙计,严格模式应该发出警告,表明它在开发环境中执行此操作。我真的希望它不会默默地这样做 (40认同)
  • 对我来说就是 React.StrictMode。感谢您指出了这一点! (6认同)
  • 还有一个考虑因素:仔细检查您是否在严格模式下使用 React。如果是这样,请参阅此处:/sf/ask/4287806071/ (2认同)
  • 但没有任何地方指定 StrictMode 也会导致 useEffect 运行两次。严格模式用于检测我们是否在任何应该是纯的函数中产生副作用,因此只有那些需要纯的函数才会运行两次,但由于 useEffect 可能包含副作用,因此应该在严格模式下运行两次。有关于此的任何文档说明 useEffect 也运行了两次吗? (2认同)

小智 27

请检查你的index.js

  <React.StrictMode>
    <App />
  </React.StrictMode>
Run Code Online (Sandbox Code Playgroud)

删除 <React.StrictMode> 包装器,您现在应该触发一次

root.render(
    <App />
);
Run Code Online (Sandbox Code Playgroud)

  • 删除 StrictMode 是最后要做的事情。它的存在是有目的的。请参阅/sf/ask/5056672281/ Called-two-times-on-mount (2认同)

小智 26

反应根>index.js>删除<React.StrictMode>包装

  • 正如目前所写的,您的答案尚不清楚。请[编辑]添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以[在帮助中心](/help/how-to-answer)找到有关如何写出好的答案的更多信息。 (8认同)
  • 这对我有用。我只是要投票赞成。 (2认同)

小智 25

如果您使用 Next js,请将reactStrictMode从“true”更改为false:

将其添加到您的 next.config.js

reactStrictMode: false,
Run Code Online (Sandbox Code Playgroud)

  • 删除 StrictMode 是最后要做的事情。它的存在是有目的的。请参阅/sf/ask/5056672281/ Called-two-times-on-mount (2认同)

Riy*_*iya 19

这是ReactJS的特性,而我们使用React.StrictMode。StrictMode 为其后代节点激活额外的检查和警告。因为如果代码中有任何不良实践,应用程序不应该崩溃。我们可以说 StrictMode 是一种安全检查,用于验证组件两次以检测错误。

您将在组件的根部获得此 <React.StricyMode> 。

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

如果你想限制组件渲染两次,你可以删除<React.StrictMode>并选中它。但有必要用于StrictMode检测运行时错误,以防代码实践不良。


Hem*_*ant 10

我在 React 18 中找到了两次组件安装背后的非常好的解释。UseEffect 在 React 中调用了两次

注意:在生产中,它运行良好。在开发环境的严格模式下,故意添加两次挂载来处理错误和所需的清理。


小智 8

新的 React 文档(目前处于测试版)有一个部分精确描述了这种行为:

\n

如何处理开发中效果两次触发

\n

来自文档:

\n
\n

通常,答案是实现清理功能。清理函数应该停止或撤消 Effect 正在执行的任何操作。经验法则是,用户应该\xe2\x80\x99t能够区分运行一次的效果(如在生产中)和设置\xe2\x86\x92清理\xe2\x86\x92设置序列(如你\xe2\x80\x99d 参见开发中)。

\n
\n

所以这个警告应该让你仔细检查你的 useEffect,通常意味着你需要实现一个清理功能。

\n


小智 8

React严格模式在开发服务器上渲染组件两次。

因此,您必须从 index.js 中删除 StrictMode。您的 index.js 当前代码如下。

root.render(
 <React.StrictMode>
 <App />
 </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

删除 StrictMode 后,它应该如下所示

root.render(
 <App />
);
Run Code Online (Sandbox Code Playgroud)


ßia*_*rol 5

我用这个作为我的替代方案useFocusEffect。我使用了嵌套的反应导航堆栈,例如选项卡和抽屉,并且重构使用useEffect对我来说并不像预期的那样有效。

import React, { useEffect, useState } from 'react'
import { useFocusEffect } from '@react-navigation/native'

const app = () = {

  const [isloaded, setLoaded] = useState(false)


  useFocusEffect(() => {
      if (!isloaded) {
        console.log('This should called once')

        setLoaded(true)
      }
    return () => {}
  }, [])

}
Run Code Online (Sandbox Code Playgroud)

此外,还有一个例子是您在屏幕上导航两次。