本文主要介绍JavaScript(JS) React 组件中,调用两次console.log(),只执行一次的原因,以及相关的示例代码。

示例代码:

import { StrictMode } from "react"
import ReactDOM from "react-dom"
function Test(): React.ReactElement {
console.log('render')
Promise.resolve()
.then(() => console.log('then ' + Math.random()))
return <></>
}
ReactDOM.render(
<StrictMode>
<Test />
</StrictMode>,
document.getElementById("root")
)

输出信息:

20:44:20.264 render
20:44:30.267 then 0.5430662800781927
40:44:30.267 then 0.9662426372351125

原因:

在React strict模式下,React可以多次运行render,这可以部分解释你所看到的内容。

在某些情况下,React会修改console.log()等控制台方法以使日志静默。

从React 17开始,React会自动修改console.log()等控制台方法,在对生命周期函数的第二次调用中关闭日志。但是,在可以使用解决方案的某些情况下,它可能会导致不希望的行为。

显然,当从Promise回调调用console.log时,它不会这样做。但当从render调用时,它会这样做。

当启用了严格模式(仅在开发模式下)时,会有第二次运行渲染函数,在第二次(同步)运行期间,React 会在第二次(同步)运行期间对console方法(调用disableLogs();)进行monkey patch,因此它不会输出。修改日志输出,这段代码被插入到packages/react-reconciler/src/ReactFiberBeginWork.js中,如下,

  if (__DEV__) {
ReactCurrentOwner.current = workInProgress;
setIsRendering(true);
nextChildren = renderWithHooks(
current,
workInProgress,
render,
nextProps,
ref,
renderExpirationTime,
);
if (
debugRenderPhaseSideEffectsForStrictMode &&
workInProgress.mode & StrictMode
) {
disableLogs(); // <--
try { // <--
nextChildren = renderWithHooks(
current,
workInProgress,
render,
nextProps,
ref,
renderExpirationTime,
);
} finally { // <--
reenableLogs(); // <--
}