Home > Software design >  add multiple input field dynamically in react
add multiple input field dynamically in react

Time:12-08

I have is a div that contains three input elements and a remove button. what is required is when the user clicks on the add button it will add this div dynamically and when the user clicks on the remove button it will remove this div. I was able to add one input element(without div container) dynamically with the following method.

  • create an array in the state variable.
  • assign a name to the dynamic input field with the help of array indexing like name0, name1

but I do not have an idea, how can I do with these many input fields. and the problem grows further when I create this whole div as a separate component. Please help. I am using a class-based component.

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

render()
  {
    return(
      <div className="row">
        <button type="button" onClick={this.addElement}>Add</button>
        <div className="col-md-12 form-group">
          <input type="text" className="form-control" name="name" value={this.state.name} onChange={this.handleChange} />
          <input type="text" className="form-control" name="email" value={this.state.email} onChange={this.handleChange} />
          <input type="text" className="form-control" name="phone" value={this.state.phone} onChange={this.state.phone} />
          <button type="button" onClick={this.removeElement}>Remove</button>
        </div> 
      </div>
    )
  }

CodePudding user response:

It was a little difficult to write down every detail of how to generate what you want. So I find it much easier to ready a stackblitz link for you to see how is it going to do this and the link is ready below:

generating a dynamic div adding and removing by handling inputs state value

CodePudding user response:

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>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

I hope this would be help for your question. I made a child component which have three input tags.

// parent component
import React, { Component } from "react";
import TextField from "./TextField";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      users: [
        {
          key: Date.now(),
          name: "",
          email: "",
          phone: ""
        }
      ]
    };
  }

  onChange = (inputUser) => {
    this.setState((prevState) => {
      const newUsers = prevState.users.map((element) => {
        if (element.key === inputUser.key) return inputUser;
        return element;
      });
      return { users: newUsers };
    });
  };

  addElement = () => {
    const { name, email, phone } = this.state;
    this.setState((prevState) => ({
      users: prevState.users.concat({
        key: Date.now(),
        name,
        email,
        phone
      })
    }));
  };

  removeElement = (id) => {
    this.setState((prevState) => ({
      users: prevState.users.filter((user) => user.key !== id)
    }));
  };

  render() {
    const { users } = this.state;
    return (
      <div className="row">
        <button type="button" onClick={this.addElement}>
          Add
        </button>
        <div className="col-md-12 form-group">
          {users.map((user) => (
            <React.Fragment key={user.key}>
              <TextField
                value={user}
                onChange={(inputUser) => this.onChange(inputUser)}
              />
              <button
                type="button"
                onClick={() => this.removeElement(user.key)}
                disabled={users.length <= 1}
              >
                Remove
              </button>
            </React.Fragment>
          ))}
        </div>
      </div>
    );
  }
}

export default App;


// child component
import { Component } from "react";

class TextField extends Component {
  handleChange = (ev) => {
    const { name, value } = ev.target;
    this.props.onChange({
      ...this.props.value,
      [name]: value
    });
  };

  render() {
    const { value: user } = this.props;
    return (
      <>
        <input
          className="form-control"
          name="name"
          value={user.name}
          onChange={this.handleChange}
          placeholder="name"
          type="text"
        />
        <input
          className="form-control"
          name="email"
          value={user.email}
          onChange={this.handleChange}
          placeholder="email"
          type="text"
        />
        <input
          className="form-control"
          name="phone"
          value={user.phone}
          onChange={this.handleChange}
          placeholder="phone"
          type="text"
        />
      </>
    );
  }
}

export default TextField;

You can also check the code in codesandbox link below. https://codesandbox.io/s/suspicious-heisenberg-xzchm

  • Related