31

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

Vencovsky
  • 28,550
  • 17
  • 109
  • 176

6 Answers6

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.

SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
  • 1
    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? – Parviz Pirizade Jan 03 '22 at 21:41
  • 3
    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.` https://reactjs.org/docs/hooks-reference.html#functional-updates – Joyce Lee Feb 24 '22 at 07:42
  • 6
    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 `false` – Cuong Vu Mar 17 '22 at 13:00
16

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/

Vlad R
  • 1,851
  • 15
  • 13
  • 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". – Ricola May 09 '22 at 13:02
  • 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. – Vlad R May 10 '22 at 05:15
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>
  );
}
lry
  • 728
  • 3
  • 7
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>
  );
}
Alaa El Saedy
  • 368
  • 3
  • 13
  • I thought it's because of Strict Mode's double-render, but looks like it's not the case: https://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? – Lisenish Oct 28 '22 at 14:17
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.

lokeshj
  • 888
  • 6
  • 11
0

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

https://stackoverflow.com/a/55374111/17262218

Barry
  • 33
  • 5
  • 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 Review](/review/late-answers/33519516) – pitamer Dec 31 '22 at 05:52