[英]Why is `Promise.then` called twice in a React component but not the 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:下面的两个答案都很棒。 我想在这里引用@user56reinstatemonica8 的评论:
相关:关于控制台静音的社区反馈
在阵营严格的模式反应可以运行呈现多次,这可以部分解释你所看到的。
但是您正确地想知道是否是这种情况并且 render 被多次调用,为什么render
没有打印两次?
在某些情况下,React 会修改诸如console.log()
类的控制台方法来使日志静音。 这是一个报价:
从 React 17 开始,React 自动修改控制台方法,如 console.log() 以在第二次调用生命周期函数时使日志静音。 但是,在可以使用变通方法的某些情况下,它可能会导致不希望的行为。
显然,当从 Promise 回调调用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);
};
确保仅将其置于开发模式(例如,在 create-react-app 中process.env.NODE_ENV === 'development'
),这样它就不会在生产版本中结束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.