React Hooks render twice


I define a scene: we have a component that uses parent's props and itself state.

There are two Components DC and JOKER and my step under the below:

  1. click DC's button
  2. DC setCount
  3. JOKER will render with the old state
  4. running useEffect and setCount
  5. JOKER does render again

enter image description here

I want to ask why JOKER render twice(step 3 and 5) and the first render squanders the performance. I just do not want step 3. If in class component I can use componentShouldUpdate to avoid it. But Hooks has the same something?

My code under the below, or open this website https://jsfiddle.net/stephenkingsley/sw5qnjg7/

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

function JOKER(props) {
  const [count, setCount] = useState(props.count);
  useEffect(() => {
    console.log('I am JOKER\'s useEffect--->', props.count);
    setCount(props.count);
  }, [props.count]);

  console.log('I am JOKER\'s  render-->', count);
  return (
    <div>
      <p style={{ color: 'red' }}>JOKER: You clicked {count} times</p>
    </div>
  );
}

function DC() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => {
        console.log('\n');
        setCount(count + 1);
      }}>
        Click me
      </button>
      <JOKER count={count} />
    </div>
  );
}

ReactDOM.render(<DC />, document.querySelector("#app"))

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. -- gaearon

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


I'm not sure I understand your question, but here goes.

When your <DC /> component changes state, it passes the new state value count to the component Joker. At this point the component will rerender, accounting for the first change.

Then you bind the effect to props.count changes;

  useEffect(() => {
    console.log('I am JOKER\'s useEffect--->', props.count);
    setCount(props.count); 
  }, [props.count]);// <-- This one

Which triggers when the component gets the new value from the component DC. It will set the state of it self Joker to props.count, which causes the component to rerender.

Which then gives you the following output:

I am JOKER's  render--> 1 // Initial render where Joker receives props from DC
index.js:27 I am JOKER's useEffect---> 2 // The hook runs because props.count changed
index.js:27 I am JOKER's  render--> 2 // Joker rerenders because its state updated.

If we just want to do the same something alike componentShouldUpdate, we can use useMemo.

function DC() {
  const [count, setCount] = useState(0);
  const [sum, setSum] = useState(0);
  const memoizedJOKER = useMemo(() => <JOKER count={count} />, [count]);
  return (
    <div>
      <button onClick={() => {
        // setCount(count + 1);
        setSum(sum + 1);
        console.log('---click---');
        console.log('\n');
      }}>
        Click me
      </button>
      <p>DC: You clicked {count} times</p>
      <p>now this is {sum} times</p>
      {memoizedJOKER}
    </div>
  );
}

When you click button, JOKER does not render again.