skip to Main Content
export const App = (props) => {
  const [data, setData] = useState(0)
  console.log(data)
  const onClick = () => {
    setData(1)
  }
  return <button onClick={onClick}>button</button>
}

From the above example, When I click button more than 2 times, I got result.

And also this code is not in <StrictMode>

0
1
1
(‘1’ printed twice even though setState same value)

Reproduced here ~ https://codesandbox.io/s/bold-glitter-zphpwr?file=/src/App.js

You can see that the render count doesn’t increase but there’s one extra log line for console.log("1: ", data)

Same result in below example.

export const App = (props) => {
  const [data, setData] = useState(0)
  console.log(data)
  useEffect(() => {
    setData(1)
  }, [data])

  return <></>
}

and In other case below, I got infinite loop.

export const App = (props) => {
  const [data, setData] = useState(0)
  console.log(data)
  setData(1)
  
  return <button onClick={onClick}>button</button>
}

I don’t know why the result came out like this.

2

Answers


  1. Adding some extra logging shows what’s going on…

    export default function App() {
      const [data, setData] = useState(0);
      const renderCountRef = useRef(0);
      renderCountRef.current++;
    
      useEffect(() => {
        console.log("data changed to", data);
      }, [data]);
    
      console.log("body data: ", data);
      console.log("body renderCount: ", renderCountRef.current);
      const onClick = () => {
        setData(1);
      };
      return (
        <div>
          <button onClick={onClick}>button</button>
          <pre>renderCount = {renderCountRef.current}</pre>
          <pre>data = {data}</pre>
        </div>
      );
    }
    

    Edit bold-glitter

    You can see that clicking the button a second time causes the following to be logged

    body data: 1
    body renderCount: 3
    

    However the displayed renderCountRef.current doesn’t move beyond 2.

    React appears to be executing a render but then determines nothing needs to change and discards the result.

    From the (legacy) docs

    Bailing out of a state update

    If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects.

    Just like how <StrictMode> causes your effect hooks to execute multiple times, your app should be resilient to extra renders.

    Login or Signup to reply.
  2. This is React’s expected behaviour. React tries to bail out of useless renders. But there might be times that it cannot judge it and would re-render anyway to be safe.

    From the legacy docs:

    Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with useMemo.

    Look at this sandbox. Here I have updated your code to add a child component. As you can see even though there is an `un-necessary’ re-render on the parent. The update is not going deeper into the tree.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search