Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setTimeout callback called twice in React functional component?

Consider the following code:

import React from "react";

function App() {
  console.log("render");

  setTimeout(() => {
    console.log("time is up");
  }, 2000);

  return <div>nothing to see here</div>;
}

export default App;

I expected the following output:

render
time is up

But the real output in the Chrome console is:

enter image description here

Note the 2 before time is up, showing us that time is up was output twice.

I don't understand why time is up is output twice. Can anyone explain this?

like image 564
stefan.at.wpf Avatar asked Sep 01 '25 02:09

stefan.at.wpf


1 Answers

The component is rendered twice because CRA sets React's strict mode by default, which among other things tries to help you detect side effects (emphasis mine):

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

So far, this is covered by posts like:

  • Why my simple react component print console twice?
  • Why is console.log logging twice in react js?
  • Why does useState cause the component to render twice on each update?

However, you might then expect both "render" and "time is up" to be logged twice. The reason that doesn't happen, which as far as I can find hasn't been covered yet on SO, is that React was updated to deliberately suppress the duplicate logs:

This disables console.log by temporarily patching the global console object during the second render pass when we double render in strict mode in DEV.

This applies only to console.logs directly in the functions mentioned above, which doesn't include setTimeout callbacks, so the second console.log("render") is swallowed but the second console.log("time is up") is not.

like image 148
jonrsharpe Avatar answered Sep 02 '25 15:09

jonrsharpe