Building form components have never been so complicated and complex as before. In React, creating complex UI components is very easy but some components may get very tricky and challenging. One has to wrap his head around and think out of the box to get the solution which not only solves the problem but also doesn’t affect the User experience. Here is an article on such a tricky component type, add/remove input fields dynamically in Reactjs.
Getting Started
Last week I was working on a Hotel Booking app in which I had to create a dynamic form where users can select the type of room, add the number of rooms and add the number of guests. But they can select only one room type at a time and if they needed another room type they had to click on the ‘Add More’ button.
So let us get started on how to solve it, I will be creating a dynamic form with three input fields, and if the user wants he can add a new set of these forms or remove forms, according to his need.
Prerequisites
- Basic sound knowledge of React and forms
- Bootstrap library for UI purpose (Optional)
Let us build one simple form first,
import React, { useState } from "react"; import "bootstrap/dist/css/bootstrap.css"; import {Form, Button, Row, Col} from 'react-bootstrap'; const Dynamicform = () => { const [roomType, setRoomType] = useState(''); const [roomNumber, setRoomNumber] = useState(1); const [guest, setGuest] = useState(1); const handleSubmit = (e) => { e.preventDefault(); console.log('roomType', roomType) console.log('room number', roomNumber) console.log('guest', guest) } return ( <Form> <Row> <Col xs={4}> <Form.Group controlId="formRoomType"> <Form.Label>Room Type</Form.Label> <Form.Control as="select" value={roomType} onChange={e => setRoomType(e.target.value)}> <option value='Room type 1'>Room type 1</option> <option value='Room type 2'>Room type 2</option> <option value='Room type 3'>Room type 3</option> <option value='Room type 4'>Room type 4</option> <option value='Room type 5'>Room type 5</option> </Form.Control> </Form.Group> </Col> <Col xs={4}> <Form.Group controlId="formBasicRoom"> <Form.Label>Rooms</Form.Label> <Form.Control type="number" placeholder="Select no of room" value={roomNumber} onChange={e => setRoomNumber(e.target.value > 0 ? e.target.value: 1)}/> </Form.Group> </Col> <Col xs={4}> <Form.Group controlId="formBasicGuest"> <Form.Label>Guests</Form.Label> <Form.Control type="number" placeholder="Select no of guest" value={guest} onChange={e => setGuest(e.target.value > 0 ? e.target.value: 1)} /> </Form.Group> </Col> </Row> <Button className='mt-5' variant="primary" type="submit" onClick={handleSubmit}> Submit </Button> </Form> ) } export default Dynamicform;
Also read, React Lifecycle Methods | Detail Explanation with Diagram
and form will look something like this,
Now we have created a simple react form, where the user can select his room type, add a number of rooms, and a number of guests. Now let’s first add two buttons for adding/removing a new form.
<Col className='pt-3 d-flex justify-content-between'> <Button variant="warning">Add More</Button> <Button variant="danger">Remove</Button> </Col>
and finally our form UI will look like this,
Also read, How to select only one element of a map() in React
As our UI has been completed, now we need to tweak our code a little to add functionality. In the above code, we are using 3 different states, to hold input data but now we will change it to a single state, to hold all the data inside as an array object.
const [bookRoomData, setBookRoomData] = useState([ { roomType:'', roomNumber:0, guest:0} ]);
and then we will map bookRoomData
state in the form, so later when we add another form it will automatically get reflected in the UI. Then we will add, onclick
event on Add More and Remove button, so when the user will click on Add More button handleAddFields
the function will be called and when the user will click on the Remove button handleRemoveFields
the function will be called.
<Form> { bookRoomData.map((data,i) => { return ( <Row className='mt-3' key={i}> <Col xs={4}> <Form.Group controlId="formRoomType"> <Form.Label>Room Type</Form.Label> <Form.Control as="select" value={data.roomType}> {/* <option value="gg" disabled>Select Room Type</option> */} <option value='Room type 1'>Room type 1</option> <option value='Room type 2'>Room type 2</option> <option value='Room type 3'>Room type 3</option> <option value='Room type 4'>Room type 4</option> <option value='Room type 5'>Room type 5</option> </Form.Control> </Form.Group> </Col> <Col xs={4}> <Form.Group controlId="formBasicRoom"> <Form.Label>Rooms</Form.Label> <Form.Control type="number" placeholder="Select no of room" value={data.roomNumber} /> </Form.Group> </Col> <Col xs={4}> <Form.Group controlId="formBasicGuest"> <Form.Label>Guests</Form.Label> <Form.Control type="number" placeholder="Select no of guest" value={data.guest}/> </Form.Group> </Col> </Row> ) }) } <Row> <Col className='pt-3 d-flex justify-content-between'> <Button variant="warning" onClick={handleAddFields}>Add More</Button> <Button variant="danger" onClick={handleRemoveFields}>Remove</Button> </Col> </Row> <Button className='mt-5' variant="primary" type="submit" onClick={handleSubmit}> Submit </Button> </Form>
We also need to add a name attribute to each input and handleChange
function in onChange event in each input filed to capture the changes in the input.
// we will add name attribute as roomType in select input field name="roomType" // we will add name attribute as roomNumber in room number input field name="roomNumber" // we will add name attribute as guest in guest number input field name="guest" // we will add the handleChange function to each input field onChange event onChange={event => handleChange(i,event)}
Also read, The Secret History of Women in Coding
So when the user inputs or select an option, the onChange will capture it and send it to the handleChange
function. And in the handleChange
function, we will first compare the event name and accordingly add values to respective keys in the object and then finally update the bookRoomData
.
const handleChange = (index,event) => { const values = [...bookRoomData]; if(event.target.name === 'roomType'){ values[index].roomType = event.target.value } else if(event.target.name === 'roomNumber' && event.target.value > 0){ values[index].roomNumber = event.target.value } else if(event.target.name === 'guest' && event.target.value > 0){ values[index].guest = event.target.value } setBookRoomData(values) }
Now we will move on to the handleAddFields
function which is get called whenever the user clicks on the ‘Add More’ button. In this function, we will add a new object to the array and update the state and as we are mapping the bookRoomData in the form, a new form input filed will automatically get added.
const handleAddFields = () => { const values = [...bookRoomData]; values.push({roomType:'', roomNumber:0, guest:0}) setBookRoomData(values); };
And finally, we will move on to the handleRemoveFields
function which is get called whenever the user clicks on the ‘Remove’ button. In this function, we will remove the last object from the array but only when if we have more than one form fields as we cant remove the last form and update the state and as we are mapping the bookRoomData in the form, a new form input filed will automatically get removed and the change will be reflected in the UI.
const handleRemoveFields = index => { const values = [...bookRoomData]; if(values.length > 1) values.pop(); setBookRoomData(values); };
Also read, How to select only one element of a map() in React
Final Words
I hope you like my article if this article helped you then do let me know in the comment section and share it more with your friends and colleagues. And please bookmark this website to get awesome tutorials like these, I am also writing blog series on ” Data Structure and Algorithm in JavaScript 🚀”.
You can also check the live demo with source code in the codeSandbox, I will leave the link below.
Leave a Reply