Skip to content
Advertisement

`useState`, only update component when values in object change

Problem

useState always triggers an update even when the data’s values haven’t changed.

Here’s a working demo of the problem: demo

Background

I’m using the useState hook to update an object and I’m trying to get it to only update when the values in that object change. Because React uses the Object.is comparison algorithm to determine when it should update; objects with equivalent values still cause the component to re-render because they’re different objects.

Ex. This component will always re-render even though the value of the payload stays as { foo: 'bar' }

const UseStateWithNewObject = () => {
  const [payload, setPayload] = useState({});
  useEffect(
    () => {
      setInterval(() => {
        setPayload({ foo: 'bar' });
      }, 500);
    },
    [setPayload]
  );

  renderCountNewObject += 1;
  return <h3>A new object, even with the same values, will always cause a render: {renderCountNewObject}</h3>;
};

Question

Is there away that I can implement something like shouldComponentUpdate with hooks to tell react to only re-render my component when the data changes?

Advertisement

Answer

I think we would need to see a better real life example of what you are tying to do, but from what you have shared I think the logic would need to move upstream to a point before the state gets set.

For example, you could manually compare the incoming values in a useEffect before you update state, because this is basically what you are asking if React can do for you.

There is a library use-deep-compare-effect https://github.com/kentcdodds/use-deep-compare-effect that may be of use to you in this case, taking care of a lot of the manual effort involved, but even then, this solution assumes the developer is going to manually decide (based on incoming props, etc) if the state should be updated.

So for example:

const obj = {foo: 'bar'}
const [state, setState] = useState(obj)

useEffect(() => {
   // manually deep compare here before updating state
   if(obj.foo === state.foo) return
   setState(obj)
},[obj])

EDIT: Example using useRef if you don’t use the value directly and don’t need the component to update based on it:

const obj = {foo: 'bar'}
const [state, setState] = useState(obj)

const { current: payload } = useRef(obj)

useEffect(() => {
    // always update the ref with the current value - won't affect renders
    payload = obj

    // Now manually deep compare here and only update the state if 
    //needed/you want a re render
    if(obj.foo === state.foo) return
    setState(obj)
},[obj])
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement