为什么在一个 React 组件中调用了两次‘ Promise.then’却没有调用 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")
)

它至少在 Chrome 和 Firefox 中产生以下输出:

00:46:30.264 render
00:46:30.267 then 0.5430663800781927
00:46:30.267 then 0.9667426372511254

我更希望看到相同数量的消息。我遗漏了什么?

转播: https://codesandbox.io/s/elegant-frost-dmcsl

编辑: 我知道严格模式会导致额外的渲染,但是如前所述,我希望消息的数量相同。

编辑2: 以下两个答案都很棒。我想在这里引用@user56的评论:

相关: 关于控制台静音的社区反馈

7978 次浏览

在反应 严格模式反应可能运行渲染 多个倍,这可以部分解释你所看到的。

但是你正确地想知道 如果是这种情况,渲染被多次调用,为什么 render没有打印两次

React 修改了控制台方法(如 console.log()) ,以便在某些情况下使日志静默:

从 React 17开始,React 会自动修改控制台 方法,以使第二次调用中的日志静默 但是,它可能会导致 在某些情况下可以使用变通方法。

很明显,当 console.log从极限回调调用时,它不会这样做。但是,当从赋值调用它时,它会这样做。关于这个问题的更多细节可以在@trincot 上找到。

当启用了严格模式(仅在开发模式下)时,呈现函数将进行第二次运行,但是正如所讨论的 给你,React 将在第二次(同步)运行期间调用补丁 console方法(调用 disableLogs();) ,因此它不会输出。

更改日志显示在 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();    // <--
}                    // <--

下面是您的代码的一个版本,它演示了它实际上运行两次:

var i = 0;
var myconsolelog = console.log; // Work around React's monkeypatching


function Test(): React.ReactElement {
i++;
myconsolelog(i + ". render"); // will output twice now!
Promise.resolve(i)
.then((i) => console.log(i + ". then " + Math.random()));
return <></>;
}

在我看来,这种对数抑制实际上是 很糟糕的一种设计选择。

如果它可能对任何人有帮助,您可以修补 Object.defineProperties以忽略对 console对象所做的任何更改,从而有效地防止 ReactDOM 修补 console.log

const defineProperties = Object.defineProperties;
Object.defineProperties = function (o, props) {
return o === console ? o : defineProperties(o, props);
};

确保只将它放在开发模式下(例如,当 process.env.NODE_ENV === 'development'时,在 create-response-app 中) ,这样它就不会最终出现在生产构建中。