Add multiple input field dynamically in react

Solution 1:

I would approach this from a configuration angle as it's a little more scalable. If you want to eventually change across to something like Formik or React Form, it makes the move a little easier.

Have an array of objects that you want to turn into input fields. Your main component should maintain state whether the <Form /> component is showing, and if it's visible pass in the config and any relevant handlers.

Your form component should maintain state for the inputs, and when you submit it, passes up the completed state to the parent.

const { Component } = React;

class Example extends Component {

  constructor(props) {
    super();

    // The only state in the main component
    // is whether the form is visible or not
    this.state = { visible: false };
  }

  addForm = () => {
    this.setState({ visible: true });
  }

  removeForm = () => {
    this.setState({ visible: false });
  }

  handleSubmit = (form) => {
    console.log(form);
  }

  render() {
    
    const { visible } = this.state;
    const { config } = this.props;
    
    return (

      <div>
        <button
          type="button"
          onClick={this.addForm}
        >Add form
        </button>

        {visible && (
          <Form
            config={config}
            handleSubmit={this.handleSubmit}
            handleClose={this.removeForm}
          />
        )}

      </div>
    );
  
  }

};

class Form extends Component {

  constructor(props) {
    super();
    this.state = props.config.reduce((acc, c) => {
      return { ...acc, [c.name]: '' };
    }, {});
  }

  handleChange = (e) => {
    const { name, value } = e.target;
    this.setState({ [name]: value });
  }

  handleSubmit = () => {
    this.props.handleSubmit(this.state);
  }

  render() {

    const { name, email, phone } = this.state;
    const { handleClose, config } = this.props;
        
    return (
      
      <div onChange={this.handleChange}>
        
        {config.map(input => {
          const { id, name, type, required } = input;
          return (
            <div>
              <label>{name}</label>
              <input key={id} name={name} type={type} required={required} />
            </div>
          )
        })}
        
        <button type="button" onClick={this.handleSubmit}>Submit form</button>
        
        <button type="button" onClick={handleClose}>Remove form</button>
      
      </div>
    
    );
  
  }

}

const config = [
  { id: 1, name: 'name', type: 'text', required: true },
  { id: 2, name: 'email', type: 'email', required: true },
  { id: 3, name: 'phone', type: 'phone', required: true }
];


ReactDOM.render(
  <Example config={config} />,
  document.getElementById('react')
);
input { display: block; }
label { text-transform: capitalize; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>