Set state with same value using hooks will cause a rerender?
Asked Answered
M

6

33

Using hooks, If I call setState with the same value as the state, will it rerender the component?

If yes, how can I avoid that?

e.g.

const [state, setState] = useState(foo)

...
// any where in the code
setState(foo)

Considering that foo can be any thing, such as {}, true, props.bar or a variable from out side of the component (constant).

Macaluso answered 26/12, 2019 at 14:46 Comment(0)
W
25

It won't re-render the component if you call setState with the same value.

Try this out:

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

const foo = { foo: 'bar' };

export default ({ name }) => {
  const [state, setState] = useState(foo);
  console.log("rendered!");
  useEffect(() => {
    setState(foo);
    console.log("state reset!");
  });

  const handleClick = () => {
    console.log("handleClick!");
    setState(foo);
    // setState({ ...foo, bar : 'baz' });
  }

  return (<div>
  <h1>Hello {name}!</h1>
  <button onClick={handleClick}>Click Me</button>
  </div>);
};

You'll notice that even when the button is clicked since the value has not changed. it's not re-rendering the Component. If you change the argument that you call setState with, it will re-render the component.


Here's a Code Sample for your ref.

Try commenting the first setState and un-commenting the second one inside the handleClick method to see the difference.

Westley answered 26/12, 2019 at 15:2 Comment(4)
I think, it is not re rendered, it is re evaluated by the virtual dom, and if there is a change only then re render occurs in the real dom?Fitts
Just wanted to add another reference to confirm this. The React Hooks docs mention: If your update function returns the exact same value as the current state, the subsequent rerender will be skipped completely. reactjs.org/docs/hooks-reference.html#functional-updatesDaniels
Adding a note here for the above example. If you use a literal object like this setState({ foo: 'bar' }), it will always be re-rendered even if the object looks the same. That's because each time a literal object is created, it's considered different from the old object. Just like when you compare two literal object {a: 1} === {a: 1} it returns falseSoidisant
Caveats The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call. If the new value you provide is identical to the current state, as determined by an Object.is comparison, React will skip re-rendering the component and its children. This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn’t affect your code. From: react.dev/reference/react/useState#setstateGimble
L
17

just to summarize

if your state is a primitive value(number, string, boolean, ...), then setting the same value using setState hook won't trigger a rerender. If your state is an Object or Array then it will behave differently.

https://overreacted.io/how-are-function-components-different-from-classes/
https://dmitripavlutin.com/value-vs-reference-javascript/

Lilley answered 11/11, 2021 at 12:7 Comment(2)
I don't think we should differentiate. If it's the same (same reference) object or array, either because it's declared outside of the render function or through useMemo, it won't trigger a rerender. So we can simply say "if Object.is(prevValue, newValue) returns true, it won't rerender".Burberry
true, in addition to that, as far as I know, react uses Object.is() method for comparing values in dependency arrays(useMemo, useEffect, etc). I'm assuming that it also uses that method for comparing values which you pass as a parameter to setState function and your current state.Lilley
O
3

It's a js syntax related question, Just like === operation.

let times = 0
const init = {name: 'Bob'}
function App() {
  const [state, setState] = useState(init)
  function modify() {
    setState({name: 'Bob'})
  }
  function modify2() {
    setState(init)
  }
  times ++
  return (
    <div className="App">
      <p>{ times }</p>
      <button onClick={modify}>Same Value Will Rerender</button>
      <button onClick={modify2}>Same Reference Never Rerender</button>
    </div>
  );
}

Here's a Code Sandbox

You can rewrite a wrapper method:

let times = 0
const init = {name: 'Bob'}
function App() {
  const [state, setState] = useState(init)
  function modify3() {
    setState2({ name: 'Bob' })
  }
  function setState2(value) {
    if (value.name === state.name) {
      return
    }
    setState(value)
  }
  times ++
  return (
    <div className="App">
      <p>{ times }</p>
      <button onClick={modify3}>Same Value Will Not Rerender Yet</button>
    </div>
  );
}
Outherod answered 26/12, 2019 at 15:30 Comment(0)
M
3

A bit late, but I found an odd case in which setting the state with the same value triggers re-render twice and then usually behaves. setting the state to 2 will trigger re-render twice.

any explanation?

export default function App() {
  const [state, setState] = React.useState(1);
  console.log("App rendered");
  console.log({ state });

  return (
    <div className="App">
      <button
        onClick={() => {
          setState(1);
        }}
      >
        update state with same value
      </button>
      <button
        onClick={() => {
          setState(2);
        }}
      >
        update state with different value
      </button>
      <div>state:{state}</div>
    </div>
  );
}
Misspell answered 17/8, 2022 at 9:23 Comment(1)
I thought it's because of Strict Mode's double-render, but looks like it's not the case: codesandbox.io/s/serene-lake-h34byu?file=/src/App.js:127-128. It's strange yeah, when you click the other button 2 times it allows 2 renders, then if you click the first again - it will allow 2 renders again. I'm not sure why honestly looks like react's internal implementation specifics?Wolffish
L
1

If the setState function or even useReducer Hook returns the same value as the current state, React will bail out without rendering the children or firing effects because it uses the Object.is comparison algorithm.

Loretaloretta answered 12/6, 2023 at 11:39 Comment(0)
B
0

may you read this articl , it has good info connect with the subject:

https://mcmap.net/q/407326/-what-are-the-differences-when-re-rendering-after-state-was-set-with-hooks-compared-to-the-class-based-approach

Bilestone answered 29/12, 2022 at 13:16 Comment(1)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewBorchers

© 2022 - 2024 — McMap. All rights reserved.