简体   繁体   中英

Why is React rerendering the component when the states are the same?

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?

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM