4

According to this and that question the spread operator seems to be used for updating an object managed in a useState hook.

I created a super simple example and found out, that even when the content of the object does not change, a re-render is triggered (which is clear, because the object changed):

import React from "react";

function useFriendStatus() {
  const [person, setPersonProps] = React.useState({name:'Mark',age:23});

  React.useEffect(() => {
    console.log("rerender");
    const interval = setInterval(() => {
      setPersonProps({...person});   //simply set the object again -> no content changed
      console.log('update');
    }, 1000);
    return () => clearInterval(interval);
  }, [person]);


  return person;
}

export default function App() {
  const person = useFriendStatus();

  return <div className="App">Hello World: {"" + person.name}</div>;
}

Here you see a screenshot from my profiler which shows that a re-rendering seems to be fired (even if the displayed name did not change):

enter image description here

I am wondering if this is a "good practice" as EVERYTHING seems to be re-rendered. Sometimes you get deeply nested objects from an API and breaking them down to super-simple non-object userState hooks is not possible.

Wouldn't it be better to Stringify everything?

import React from "react";


function useFriendStatus() {
  const [person, setPersonProps] = React.useState(JSON.stringify({name:'Mark',age:23}));

  React.useEffect(() => {
    console.log("rerender");
    const interval = setInterval(() => {
      const personCopy=JSON.parse(person);
      setPersonProps(JSON.stringify({...personCopy}));
      console.log('update');
    }, 1000);
    return () => clearInterval(interval);
  }, [person]);


  return person;
}

export default function App() {
  const person = JSON.parse(useFriendStatus());

  return <div className="App">Hello World: {"" + person.name}</div>;
}

How do you handle that in practice?

norbitrial
  • 14,716
  • 7
  • 32
  • 59
user3579222
  • 1,103
  • 11
  • 28

1 Answers1

2

I created a super simple example and found out, that even when the content of the object does not change, a re-render is triggered (which is clear, because the object changed)

It has nothing to do with the "content" of the object, your component re-renderers because you create a shallow copy {...person} of an object (changing its reference).

On render phase React makes a shallow comparison with the previous state in order to decide if render will occur, and in javascript, {} === {} is always false.

the spread operator seems to be used for updating an object managed in a useState hook.

As the state should be treated as immutable it is common to use the spread operator to make a shallow copy.

"It should only re-render if the name property of the object changes"

It is common to just add a condition before calling setState:

React.useEffect(() => {
  const newPerson = { ...person }; // fetch from some source
  // or check if person.name !== newPerson.name
  if (!isEqual(person, newPerson)) {
    setPerson(newPerson);
  }
}, [person]);
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118