Robin
Robin

Reputation: 109

React doesn't render all components of array

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

Answers (3)

Hemadri Dasari
Hemadri Dasari

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

Tholle
Tholle

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

Daniel Hilgarth
Daniel Hilgarth

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

Related Questions