背景
自己在尝试使用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 }
复制代码
输出结果如下:
我们可以清晰的看到222被执行了两次,而且App输出了一个深的一个灰的,我不理解这是什么原因。在我最后找了好久之后,终于在这篇回答中发现了一点问题。
这个提问在末尾说了这么一句话:我没有用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 组件的
constructor
,render
以及shouldComponentUpdate
方法 - class 组件的生命周期方法
getDerivedStateFromProps
- 函数组件体
- 状态更新函数 (即
setState
的第一个参数) - 函数组件通过使用
useState
,useMemo
或者useReducer
看到其中的函数组件体,一切都真相大白了。原因就是react脚手架启动的项目默认会用严格模式包裹,而包裹后会使组件被调用两次,纯纯的是自己给自己挖坑了属于是。
在后面还解释了对于结果中那行打印出的浅灰色的---App---
也给出了说明:
注意:在 React 17 中,React 会自动修改 console 的方法,例如
console.log()
,在第二次调用生命周期函数时,将日志静默。然而,在某些情况下,这可能会导致一些不符合期望的行为发生,此时可以使用替代解决方案。
从 React 18 开始,React 不会抑制任何日志。不过,如果你安装了 React Dev Tools,第二次调用的日志会出现被轻微淡化。React DevTools 也提供了一个设置(默认关闭)来完全抑制它们。
好吧,人家管这个浅灰色叫做轻微淡化,我说怎么后面的输出是react.devtools。至此,一切都真相大白,还好它只在开发模式下运行不会影响生产构建。
最后放上react官网关于严格模式的介绍。