Reputation: 109
I want to dynamically add Components, after clicking the "add" button. For that, I created an array that consists of all the components, and add them on click.
My problem is, that it only renders one component, even though it consists of several ones.
My code looks like this:
class QuestionBlock extends React.Component {
constructor(props) {
super(props);
this.state = {answersArray: []};
}
addPossibleAnswer() {
this.state.answersArray.push(
<PossibleAnswers id={this.state.answersArray.length + 1}/>
)
this.forceUpdate();
}
componentWillMount() {
this.state.answersArray.push(
<PossibleAnswers id={this.state.answersArray.length + 1}/>
)
}
render() {
console.log(this.state.answersArray) // Grows after adding componenets, but they are not rendered.
return (
<div>
{this.state.answersArray}
<AddPossibleAnswer addPossibleAnswer={() => this.addPossibleAnswer()} />
</div>
);
}
}
If you see what I did wrong, I'd be really glad if you could help me out!
Upvotes: 2
Views: 136
Reputation: 33974
You are directly pushing elements to the array without setState so the component won't re-render
Also avoid using tthis.forceUpdate() as much as you can in your application because this is not recommended much
You need to change your code like below. The recommended approach for dealing with arrays in react is using previous state and push to an array
addPossibleAnswer() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
componentWillMount() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
Also keep in mind that componentWillMount life cycle method is deprecated in react 16. So move the code to componentDidMount instead
Here is the corrected code
class QuestionBlock extends React.Component {
constructor(props) {
super(props);
this.state = {answersArray: []};
}
addPossibleAnswer() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
componentDidMount() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
render() {
const { answersArray } = this.state;
return (
<div>
{answersArray}
<AddPossibleAnswer addPossibleAnswer={() => this.addPossibleAnswer()} />
</div>
);
}
}
Upvotes: 1
Reputation: 112777
Instead of mutating state directly and adding JSX to it, you can instead keep raw data in your state and derive the JSX from that in the render method instead.
Example
class QuestionBlock extends React.Component {
state = { answers: 1 };
addPossibleAnswer = () => {
this.setState(({ answers }) => ({ answers: answers + 1 }));
};
render() {
return (
<div>
{Array.from({ length: this.state.answers }, (_, index) => (
<PossibleAnswers key={index} id={index} />
))}
<AddPossibleAnswer addPossibleAnswer={this.addPossibleAnswer} />
</div>
);
}
}
Upvotes: 1
Reputation: 174299
You don't interact with state
like you do. Never mutate the state field. You need to use this.setState
:
this.setState(prevState => ({answersArray: prevState.answersArray.concat([
<PossibleAnswers id={prevState.answersArray.length + 1}])}));
Having said that, it is also strange that you store components in state. Usually, you would store data and create the components based on the data in the render method.
Upvotes: 1