useEffect重复调用两次的坑

背景

自己在尝试使用react写一个date-picker组件时发现的,刚开始没太注意。后来写完后为了组件完整开始排查这个bug。

先是将代码逐行注释,发现即使我注释掉所有的代码只保留一行console仍然会执行两遍,我开始想这会不会不是我的问题。

然后开始找了一圈查阅了众多资料和网页,都没有搜到为什么一行console也能在useEffect中执行两遍的原因。

就当我准备放弃的时候,在一个回答中发现了些许的蛛丝马迹,下面先放上我的代码。

代码

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

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

// src/App.js
import { Xx } from './useTest';
function App() {
  console.log('---App---');
  return <Xx />
}
export default App;

// src/useTest/index.js
import { useEffect } from 'react';

 function Xx() {
     useEffect(() => {
         console.log("222");
     }, [])
     return <div>Hello world!</div>
 }

 export { Xx }
复制代码

输出结果如下:

1654402809790.jpg 我们可以清晰的看到222被执行了两次,而且App输出了一个深的一个灰的,我不理解这是什么原因。在我最后找了好久之后,终于在这篇回答中发现了一点问题。

react代码执行两次问题?

这个提问在末尾说了这么一句话:我没有用StrictMode,但如果我删掉这行setCount(x => x+1),就不会两次执行,为什么呢?是不是自定义Hook内部定义的State改变了也会触发使用Hook的组件重新渲染?

看到他这句话的意思我好想脑海里突然就抓到了什么,因为我是用react脚手架创建的react项目,创建后在index.js中做了修改。偶尔瞟到这个StrictMode,但是因为是脚手架生成的没有深究就开始开发了。而听这位提问者的说法,好像这个StrictMode会造成组件的重新渲染。然后我在代码中只放一个<App />组件不用他包裹,发现不会渲染两次了!!!

然后我就在网上查阅StrictMode是什么,为什么会造成这种原因?

React严格模式StrictMode

定义

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。

作用

StrictMode 目前有助于:

  • 识别不安全的生命周期
  • 关于使用过时字符串 ref API的警告
  • 关于使用废弃的 findDOMNode 方法的警告
  • 检测意外的副作用
  • 检测过时的 context API
  • 确保可复用的状态

副作用

严格模式不能自动检测到你的副作用,但它可以帮助你发现它们,使它们更具确定性。通过 故意重复调用(注意这里) 以下函数来实现的该操作:

  • class 组件的 constructorrender 以及 shouldComponentUpdate 方法
  • class 组件的生命周期方法 getDerivedStateFromProps
  • 函数组件体
  • 状态更新函数 (即 setState 的第一个参数)
  • 函数组件通过使用 useStateuseMemo 或者 useReducer

看到其中的函数组件体,一切都真相大白了。原因就是react脚手架启动的项目默认会用严格模式包裹,而包裹后会使组件被调用两次,纯纯的是自己给自己挖坑了属于是。

在后面还解释了对于结果中那行打印出的浅灰色的---App---也给出了说明:

注意:在 React 17 中,React 会自动修改 console 的方法,例如 console.log(),在第二次调用生命周期函数时,将日志静默。然而,在某些情况下,这可能会导致一些不符合期望的行为发生,此时可以使用替代解决方案。

从 React 18 开始,React 不会抑制任何日志。不过,如果你安装了 React Dev Tools,第二次调用的日志会出现被轻微淡化。React DevTools 也提供了一个设置(默认关闭)来完全抑制它们。

好吧,人家管这个浅灰色叫做轻微淡化,我说怎么后面的输出是react.devtools。至此,一切都真相大白,还好它只在开发模式下运行不会影响生产构建

最后放上react官网关于严格模式的介绍。

严格模式

猜你喜欢

转载自juejin.im/post/7105652180501135367