I'm trying to create a react app that adds a react ponent when pressing a button a then re-renders it. I'm using an array of p elements inside state as a test. The event handler function uses setState to modify the array but for some reason it does not re-renders de ponent so the change (the new element) is not showed in the screen. What I'm doing wrong?
import React, {Component} from "react";
export default class Agenda extends React.Component{
constructor(props){
super(props);
this.list = [];
this.state = {
list:[]
};
this.addItem = this.addItem.bind(this);
const items = ["Hola Mundo 1","Hola Mundo 2","Hola Mundo 3"];
const itemComponent = items.map((item) =>
<p>{item}</p>
);
this.list = itemComponent;
this.state.list = itemComponent;
}
addItem(){
//Adds a new paragraph element at the end of the array
this.list[3]= <p>Hola Mundo 4</p>;
this.setState(
{
list: this.list
}
);
}
render(){
return(
<div id={this.props.id} className={this.props.className}>
{this.state.list}
<button onClick={this.addItem}>
Add item
</button>
</div>
);
}
}
I'm trying to create a react app that adds a react ponent when pressing a button a then re-renders it. I'm using an array of p elements inside state as a test. The event handler function uses setState to modify the array but for some reason it does not re-renders de ponent so the change (the new element) is not showed in the screen. What I'm doing wrong?
import React, {Component} from "react";
export default class Agenda extends React.Component{
constructor(props){
super(props);
this.list = [];
this.state = {
list:[]
};
this.addItem = this.addItem.bind(this);
const items = ["Hola Mundo 1","Hola Mundo 2","Hola Mundo 3"];
const itemComponent = items.map((item) =>
<p>{item}</p>
);
this.list = itemComponent;
this.state.list = itemComponent;
}
addItem(){
//Adds a new paragraph element at the end of the array
this.list[3]= <p>Hola Mundo 4</p>;
this.setState(
{
list: this.list
}
);
}
render(){
return(
<div id={this.props.id} className={this.props.className}>
{this.state.list}
<button onClick={this.addItem}>
Add item
</button>
</div>
);
}
}
Share
Improve this question
edited Aug 28, 2019 at 16:41
programmer1
asked Aug 28, 2019 at 16:40
programmer1programmer1
472 silver badges8 bronze badges
6
-
1
this.state.list = itemComponent;
don't to this – Dominik Matis Commented Aug 28, 2019 at 16:42 -
1
this.list
andthis.state.list
are references to the same array which means that you're mutating the state when you do this:this.list[3] = ...
– Titus Commented Aug 28, 2019 at 16:44 - @DominikMatis Ok I see the error, I will correct it and see if that solves the problem. Thanks – programmer1 Commented Aug 28, 2019 at 16:46
-
1
You should use
setState
– Dominik Matis Commented Aug 28, 2019 at 16:47 - 1 You don't need to keep a copy of the array (see Rajat Bhatnagar answers) and you should probably keep strings in the state instead of ponents, use an array of strings and build ponents from it in the render function. – Titus Commented Aug 28, 2019 at 16:58
2 Answers
Reset to default 8There are several things you are doing which are bad practice in react. One or more of them are likely causing your problem. The issues are:
1) Don't save ponents in state. Your state should be just the minimum data that determines the ponents, and the ponents themselves show up in render. By storing ponents in state you make it easy to forget to update the state, and thus cause the rendering to not change.
2) Don't duplicate state as instance variables. By doing this, you're only forcing yourself to manually keep two pieces of data in sync with eachother. Instead, have a single source of truth, and have everything else derive from that.
3) Don't mutate state. If you want to add items to an array, create a new array which is a shallow copy of the old one, then append to that new array. React relies on state being immutable to tell whether state has changed, so mutations are an easy way to accidentally make rerendering not happen.
export default class Agenda extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ['Hola Mundo 1', 'Hola Mundo 2', 'Hola Mundo 3'],
};
this.addItem = this.addItem.bind(this);
}
addItem() {
this.setState((oldState) => {
const newList = [...oldState.list];
newList.push('Hola Mundo 4');
return { list: newList };
});
}
render() {
return (
<div id={this.props.id} className={this.props.className}>
{this.state.list.map(item =>
<p>{item}</p>
)}
<button onClick={this.addItem}>
Add item
</button>
</div>
);
}
}
this.list[3]= <p>Hola Mundo 4</p>;
The above statement does not change the reference of the list variable. So set state doesn't see a difference while rendering. Instead do something like this -
this.setState({
list: this.list.concat(<p>Hola Mundo 4</p>)
});