How to render a dynamic form in React

Viewed 1047

I am trying to render a dynamic form to the user. I am using react js. Here is my logic: I first display a number selector and a button to the user. The user has 1 - 10 to choose from with the number selector. After they choose their number, they click the button and what is SUPPOSED to happen is I want the page to render a form with the number of input fields as the selected number from the number selector, as chosen by the user. I wanted to return the input fields in a for loop. However, the

console.log(allTerms);

only prints out twice, and I only get one input field displayed on the page. It seems the loop is getting stuck as it tries to render the second input field. Here is my react component:

import React, { Component } from 'react'

export class ShowTerms extends Component {
    state = {
        numberOfTerms: '0',
        addTerms: false
    }

    addTerms = (event) => {
        event.preventDefault();
        this.setState({addTerms: true});
    }

    onChange = (event) => this.setState({ [event.target.name]: event.target.value});

    render() {
        if(this.state.addTerms === false) {
        return(
            <div>
            <h2>Choose how many terms you wish to add:</h2>
            <input type="number" name="numberOfTerms" min="1" max="10" value={this.state.numberOfTerms} onChange={this.onChange} required></input>
            <input type="button" onClick={this.addTerms}></input>
            </div>
        )
        } else if(this.state.addTerms === true) {
        var allTerms = this.state.numberOfTerms;
            for (var i = 0; i < allTerms; i++) {
                console.log(allTerms);
                return (
                    <div>
                    <input type="text" value="this is a test"></input>
                    </div>
                )  
            }
        } 
    }
}

export default ShowTerms

Any ideas as to why the for loop is only displaying one input field? Is there a mistake in my logic, or would there be a better way to approach displaying this dynamic form? Thanks in advance for any ideas.

3 Answers

You can't use a for loop in a render - it requires a returned expression, not statements like for. So you need to use a map on an array, as per the following:

const arr = new Array(this.state.numberOfTerms);
arr.map((i) => (
  <div key={i}>
     <input ...></input>
  </div>));

See https://blog.cloudboost.io/for-loops-in-react-render-no-you-didnt-6c9f4aa73778

try this

Array.from(Array(this.state.numberOfTerms)).map(()=>
   <div>
      <input type="text" value="this is a test"></input>
    </div>
)

So here is the solution I used. First, I took the value of the number that the user chose (which was stored in this.state.numberOfTerms) and used that number in a loop I added to my addTerms function, as follows:

addTerms = (event) => {
        event.preventDefault();
        this.setState({addTerms: true});
        var test = [];
        for(var i = 0; i < this.state.numberOfTerms; i++) {
            test.push(i);
        }
        this.setState({numberOfTerms: test})
    }

The idea here is that when a user selects the number they want, then click the button, this function is called. When this function is called, it runs a loop that fills an array with the same number of elements as the number the user chose. I then set that array (named test) to state (setting over the current value in this.state.numberOfTerms). Now I am free to loop through the array that is now stored in state which creates an input field for each element of my state array (which should also be the number the user chose originally). As noted by the other answers to my question, a for loop is not compatible here because the return statement terminated the for loop before it was finished. Instead, I need to map through the array. Here is my render method again, with the changes in the second return statement:

render() {
        if(this.state.addTerms === false) {
        return(
            <div>
            <h2>Choose how many terms you wish to add:</h2>
            <input type="number" name="numberOfTerms" min="1" max="10" value={this.state.numberOfTerms} onChange={this.onChange} required></input>
            <input type="button" onClick={this.addTerms}></input>
            </div>
        )
        } else if(this.state.addTerms === true) {
            return (
            this.state.numberOfTerms.map((i) => (
                <div key={i}>
                    <input type="text"></input>
                </div>
            )))
        } 
    }
Related