Home > Blockchain >  Why is React rerendering the component when the states are the same?
Why is React rerendering the component when the states are the same?

Time:09-22

I have an object state inside my react component set up like this:

const [state, setState] = useState({ keyOne: true, keyTwo: 'someKey' })

When I change the state, the component obviously rerenders. But that's the problem. The component is rerendering even if I change the state to the value that it's currently set to. For example, the component still rerenders if I do this:

setState({ keyOne: true, keyTwo: 'someKey' });

I'm guessing this is because I'm passing a new object to setState, and so it will be interpreted as a different state each time even if the content in the object is the same. So I guess my question is:

How can I prevent a component from re-rendering if the new object state has the same content as the original state? Is there a built-in way to do this, or will I have to compare the objects beforehand myself?

CodePudding user response:

It's rerendering because the new object is a different object from the previous object. It may have all the same properties inside of it, but react only does a shallow comparison.

If you want to skip the render, you will need to make sure the object reference does not change. For example:

setState(prev => {
  if (prev.keyOne === true && prev.keyTwo === 'someKey')  {
    return prev; // Same object, so render is skipped
  } else {
    return { keyOne: true, keyTwo: 'someKey' };
  }
});

If you have some favorite library for checking deep equality (Eg, lodash or ramda), you could shorten it to something like the following. This will be particularly useful if there are a lot of properties you would otherwise need to check:

setState(prev => {
  const next = { keyOne: true, keyTwo: 'someKey' };
  return R.equals(prev, next) ? prev : next;
});

CodePudding user response:

One option is to use separate states for the different properties of the object. That way, a re-render will only be triggered once one of the new state values is different. For a small example:

const App = () => {
    console.log('rendering');
    const [val, setVal] = React.useState(true);
    React.useEffect(() => {
        setVal(true);
    }, []);
    return 'foo';
};

ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>

As you can see, despite the state setter being called, the component doesn't re-render, because the new state is the same as the old state.

For your code, you'd have something like

const [keyOne, setKeyOne] = useState(true);
const [keyTwo, setKeyTwo] = useState('someKey');

Using an approach like the above instead of putting state into a single object is a very common practice with functional components.

  • Related