React useState cause double rendering

Put the console.log in an useEffect hook without dependencies and you'll see it isn't actually rendering twice.

import React, { useEffect, useState } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(count);
  });
  
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      count: {count}
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default MyComponent;

Edit wonderful-tesla-cf8uu

Here's a good diagram of the component lifecycle, it lists the class-based lifecycle functions, but the render/commit phases are the same.

enter image description here

The import thing to note is that the component can be "rendered" without actually being committed (i.e. the conventional render you see to the screen). The console.log alone is part of that. The effects run after in the "commit" phase.

useEffect

... The function passed to useEffect will run after the render is committed to the screen. ...

By default, effects run after every completed render, ...

React Strict Mode

Detecting Unexpected Side-effects

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

This only applies to development mode.


Some more info on double re-rending (as per a closed react issue).

It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.

https://github.com/facebook/react/issues/15074

another related question is here.

useState() do double render