galah92
galah92

Reputation: 3991

React useState cause double rendering

Consider the canonical useState example:

import React, { useState } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);
  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;

Clicking the button makes each state print twice. Why is that?

screenshort

Upvotes: 8

Views: 6496

Answers (3)

Clearives
Clearives

Reputation: 1

const countRef = useRef(1);
countRef.current += 1;

This way render will execute 2 times at a time


const countRef = useRef(1);
useEffect(() => {
   countRef.current += 1;
})

This way render will execute 1 times at a time

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202605

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.

Upvotes: 15

gdh
gdh

Reputation: 13682

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

Upvotes: 3

Related Questions