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