React:useEffect为什么会在组件挂载时执行两次回调?

997 阅读1分钟

1、奇怪现象

在开发环境的严格模式下:以下两种情况effect回调都会执行两次

第一次执行effect回调时会打印第一个"组件挂载了",第二次执行时会先对上次的执行进行清理,所以打印"组件卸载了",最后第二次执行effect回调打印第二个"组件挂载了"

// 情况一
useEffect(() => {
  console.log('组件挂载了')
  return () => {
    console.log('组件卸载了')
  }
})

// 情况二
useEffect(() => {
  console.log('组件挂载了')
  return () => {
    console.log('组件卸载了')
  }
}, [])

image.png

在生产环境中不会出现这种现象

2、官方为何会这样做?

原因:为了通过挂载两次组件来提早发现你的问题。

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  }, [])

  return (
    <div>count: {count}</div>
  )
}

export default App;

上面这段代码中的App组件会挂载两次,同时有两个定时器运行,导致页面上的count会以count+2递增显示。 这个时候你就会发现代码有问题,从而你会主动加上清除定时器代码。如下。

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)

  +  return () => clearInterval(timer)
  }, [])

  return (
    <div>count: {count}</div>
  )
}

export default App;

此时count的显示就正常了,以count+1递增显示。

另一种解决办法是不使用严格模式,如下

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-  //<React.StrictMode>
    <App />
-  //</React.StrictMode>
);

此时effect的表现在开发环境和生产环境保持一致。

总结

当你发现effect回调执行两次时,其实是React官方在通知你:你的代码不够好,赶紧优化一下吧!!!