反应挂钩:useEffect()被调用两次,即使使用空数组作为参数

时间:2020-03-10 13:40:57

标签: 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;

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

4 个答案:

答案 0 :(得分:10)

将console.log放在useEffect内

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

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

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

此组件在您的页面中多次出现

这一点应该很明显,您的组件在页面中出现了两次,每个组件都将挂载并运行useEffect

树的上方正在卸载并重新安装

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

答案 1 :(得分:2)

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

<块引用>

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

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

Strict Mode - Reactjs docs

这里有类似的问题My React Component is rendering twice because of Strict Mode

答案 2 :(得分:1)

不确定为什么不将结果置于状态,下面是一个示例,该示例只调用一次效果,因此您必须在未发布的代码中做了一些操作,使其再次呈现:

const App = () => {
  const [isLoading, setLoad] = React.useState(true)
  const [data, setData] = React.useState([])
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        setLoad(false)//causes re render
        setData(data)//causes re render
      })
  },[])
  //first log in console, effect happens after render
  console.log('rendering:', data.length, isLoading)
  return <pre>{JSON.stringify(data, undefined, 2)}</pre>
}

//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

为防止多余的渲染,您可以在一种状态下合并数据并加载:

const useIsMounted = () => {
  const isMounted = React.useRef(false);
  React.useEffect(() => {
    isMounted.current = true;
    return () => isMounted.current = false;
  }, []);
  return isMounted;
};


const App = () => {
  const [result, setResult] = React.useState({
    loading: true,
    data: []
  })
  const isMounted = useIsMounted();
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        //before setting state in async function you should
        //  alsways check if the component is still mounted or
        //  react will spit out warnings
        isMounted.current && setResult({ loading: false, data })
      })
  },[isMounted])
  console.log(
    'rendering:',
    result.data.length,
    result.loading
  )
  return (
    <pre>{JSON.stringify(result.data, undefined, 2)}</pre>
  )
}

//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 3 :(得分:0)

我遇到过这样的问题:

const [onChainNFTs, setOnChainNFTs] = useState([]);

会触发这个 useEffect 两次:

useEffect(() => {
    console.log('do something as initial state of onChainNFTs changed'); // triggered 2 times
}, [onChainNFTs]);

我确认组件 MOUNTED ONLY ONCE 和 setOnChainNFTs 没有被多次调用 - 所以这不是问题。

我通过将 onChainNFT 的初始状态转换为 null 并进行空检查来修复它。

例如

const [onChainNFTs, setOnChainNFTs] = useState(null);
useEffect(() => {
if (onChainNFTs !== null) {
    console.log('do something as initial state of onChainNFTs changed'); // triggered 1 time!
}
}, [onChainNFTs]);