Why is `Promise.then` called twice in a React component but not the console.log? Why is `Promise.then` called twice in a React component but not the console.log? reactjs reactjs

Why is `Promise.then` called twice in a React component but not the console.log?


In React strict mode react may run render multiple times, which could partly explain what you see.

But you correctly wondered if that was the case and render was called multiple times, why was render not printed twice too?

React modifies the console methods like console.log() to silence the logs in some cases. Here is a quote:

Starting with React 17, React automatically modifies the consolemethods like console.log() to silence the logs in the second call tolifecycle functions. However, it may cause undesired behavior incertain cases where a workaround can be used.

Apparently, it doesn't do so when the console.log is called from Promise callback. But it does so when it is called from render. More details about this are in the answer by @trincot.


There is a second run of your render function when strict mode is enabled (only in development mode), but as discussed here, React will monkey patch console methods (calling disableLogs();) for the duration of that second (synchronous) run, so that it does not output.

The changelog shows this code was inserted in packages/react-reconciler/src/ReactFiberBeginWork.js in order to temporarily suppress logs (insertions marked with comment):

  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();    // <--      }                    // <--

Here is a version of your code that demonstrates it really runs twice:

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 <></>;}

In my opinion, this log-suppression is a really bad design choice.