簡體   English   中英

為什么 `Promise.then` 在 React 組件中被調用兩次,而不是在 console.log 中?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM