Vencovsky
Vencovsky

Reputation: 31585

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

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

Upvotes: 35

Views: 21737

Answers (7)

Fi1osof
Fi1osof

Reputation: 1

Looks like react enforce update state twice. So if you set same value and new value, got one more rerender. After, set same value have no effect. Have a look modified stackblitz

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

// const foo = { foo: 'bar' };
let foo = 1
let clicked = 0;

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

  const handleClick = () => {
    console.log("handleClick!");
    clicked++;
    
    if(clicked%3 === 0) {
      foo++
    }

    setState(foo);
    // setState({ ...foo, bar : 'baz' });

  }

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


Upvotes: 0

lokeshj
lokeshj

Reputation: 948

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.

Upvotes: 1

Barry
Barry

Reputation: 43

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

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

Upvotes: 0

Alaa
Alaa

Reputation: 418

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>
  );
}

Upvotes: 3

Vlad R
Vlad R

Reputation: 2624

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/

Upvotes: 17

lry
lry

Reputation: 748

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>
  );
}

Upvotes: 3

SiddAjmera
SiddAjmera

Reputation: 39432

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.

Upvotes: 26

Related Questions