Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update object data-members in React state doesn't trigger re-rendering

I'm using TypeScript and React to try to implement my custom types in React component. However the component is not re-rendering if data members of object in state has updated.

Here's my code:

class Person {
  name: string;
  surname: string;

  constructor(name: string, surname: string) {
    this.name = name;
    this.surname = surname;
  }
}

class App extends Component<AppProps, AppState> {
  constructor(props) {
    super(props);
    this.state = {
      persons: [new Person("Alpha", "A"), new Person("Bravo", "B")]
    };
  }

  render() {
    return (
      <div>
        <button
          onClick={() => {
            this.state.persons[0].name = "Zulu";
            this.state.persons[0].surname = "Z";
          }}
        >
          Change A to Z
        </button>

        <button
          onClick={() => {
            this.setState({
              persons: [...this.state.persons, new Person("Charlie", "C")]
            });
          }}
        >
          Add C
        </button>

        <button onClick={() => { this.forceUpdate(); }}> Force Update </button>

        {this.state.persons.map(person => (
          <p>
            {person.name} {person.surname}
          </p>
        ))}
      </div>
    );
  }
}

Full Code Snippet

If I click on Change A to Z nothing will change - unless Add C or Force Update is clicked. I am assuming React cannot detect changes made in Person therefore no re-render.

I have some questions about this problem.

  • Is this approach (custom datatype as state) recommended to use in React
    • If yes - how do I make this work? (React able to detect changes made in Person)
    • If no - what is the recommended approach to use custom datatype in React?
like image 891
TacticalBacon Avatar asked Mar 14 '26 12:03

TacticalBacon


1 Answers

React will not re-render by default if every element in the new state is === to every element in the old state. You also have to call setState in order to trigger a re-render. Your Change A to Z is failing on both counts - you need to both create a new persons array without mutating what's currently in state, and then call setState with the new array.

Also, having a Person instance doesn't look to be accomplishing anything, since there are no methods on the class - and it'll make changing the state harder, so I'd remove Person entirely. Try

  constructor(props) {
    super(props);
    this.state = {
      persons: [{ name: 'Alpha', surname: 'A' }, { name: 'Bravo', surname: 'B' }]
    };
  }

Then change

      onClick={() => {
        this.state.persons[0].name = "Zulu";
        this.state.persons[0].surname = "Z";
      }}

to

onClick={() => {
  this.setState({
    persons: [{ name: 'Zulu', surname: 'Z' }, ...this.state.persons.slice(1)]
  });
}}

The

persons: [{ name: 'Zulu', surname: 'Z' }, ...this.state.persons.slice(1)]

effectively immutably replaces the first element of the array with the new Zulu object.

like image 124
CertainPerformance Avatar answered Mar 16 '26 02:03

CertainPerformance



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!