15

I have sample code below:

function App() {
  console.log("render");
  const [val, setVal] = React.useState(0);
  return (
    <div className="App">
      <h1>{val}</h1>
      <button onClick={() => setVal(12)}>Update with same value</button>
    </div>
  );
}

When I click a button multiple times, the console log 3 times with 'render' message. For me, it should be 2 times only:

  • 1 for first render

  • 2 for the update from val 0 to 12 (when click button)

and since this time, it should not re-render because the same value (12) is updated to val.

But why it appears 3 times? That mean it still re-render one more time despite the same value was updated.

Anyone who know please explain this, thanks in advance.

P/S: I've figured out that it's only cause an extra re-render when the value changed then has been updated with the same

function App() {
  console.log("render");
  const [val, setVal] = useState(4);
  return (
    <div className="App">
      <h1>{val}</h1>
      <button onClick={() => {
        setVal(val => val + 1)
      }}>Update</button>
      <button onClick={() => {
        setVal(val => val)
      }}>Update with same value</button>
    </div>
  );
}

When first click on 2nd button, no re-render call, but if you click the 1st button then 2nd button, 2nd button cause 1 extra re-render

Quoc Van Tang
  • 1,075
  • 4
  • 15
  • 33

2 Answers2

12

This thread may help you : React: Re-Rendering on Setting State - Hooks vs. this.setState

Also, you can check the second paragraph over here which says:

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.

Yash Joshi
  • 2,586
  • 1
  • 9
  • 18
11

React can’t guess the ouput of render() won’t change: it has to render() again and compare the results with the previous render().

Then the magic happens: if there are no differences, the DOM is not updated; if there are differences, it tries to only create/destroy elements as needed, because that’s the expensive part, not running render() — well it should not be.

Changing the state normally triggers a call to render() (not necessarily DOM modifications) — but if you want control over that behavior, define shouldComponentUpdate.


Note: That goes for non-hook components. However, I didn’t know the behavior of hooks was slightly different from that of a regular component: it seems that you’re right in expecting setState not to trigger a render when the value is unchanged — see Yash Joshi's answer.

hugo
  • 3,067
  • 2
  • 12
  • 22
  • We can also use `PureComponent` in Class Component instead of manually defining `shouldComponentUpdate`. But prefer `shouldComponentUpdate` when the data is deeply nested. – Yash Joshi Aug 26 '19 at 05:55
  • 1
    Yes but I still dont know why react need to call render one more time again when the value is not change. – Quoc Van Tang Aug 26 '19 at 05:57
  • In the case of a non-hook, it’s expected: React chooses to compare the old/new render() ouputs rather than the old/new states. In your case (hook), the docs says only the rendering of the children is guaranteed to be skipped if the value is unchanged. As to *why* it might still need to re-render the hook, I don’t have an explanation... – hugo Aug 26 '19 at 06:10
  • 4
    Do check this comment link if you are curious to know the reason behind the additional re-render: https://github.com/facebook/react/issues/14810#issuecomment-462089702 – Yash Joshi Aug 26 '19 at 06:18
  • I've read that comment and figured out that it's only cause 1 extra re-render when the value changed then update with same value again (I edit my post). But honestly, still didn't know why it needs to call render 1 more time :( – Quoc Van Tang Aug 26 '19 at 07:03