kushal verma
kushal verma

Reputation: 184

Dynamically rendering form elements and saving them in an array in ReactJS

I am having 3 materialUI TextFields which I want to render n number of times (that n will be a integer input from user before rendering the form field, here I stored it in a variable named groupMembersCount). I am using functional component in ReactJS defining an array with useState hooks as:

const [groupDetails, setGroupDetails] = React.useState([
    { fullName: "", phoneNo: "", gender: "" },
  ]);

This will be my main array to hold all the 3 values(user input) at their respective indexes. I tried returning those 3 materialUI TextField dynamically by this method:

{Array.apply(null, { length: groupMembersCount }).map(
                (e, i) => (
                  <div key={i}>
                    <strong>Member #{i + 1}</strong>
                    <div className="getIndex" name={i + 1}>
                      <TextField
                        id={Math.random()}
                        name="Name"
                        variant="outlined"
                        margin="none"
                        label="Name"
                        onChange={handleGroupName}
                      />
                      <TextField
                        id={Math.random()}
                        name="phoneNo"
                        variant="outlined"
                        margin="none"
                        label="Mobile Number"
                        onChange={handleGroupPhoneNo}
                      />
                      <TextField
                        id={Math.random()}
                        name="gender"
                        variant="outlined"
                        margin="none"
                        label="Gender"
                        onChange={handleGroupGender}
                        select
                      >
                        <option value="MALE">Male</option>
                        <option value="FEMALE">Female</option>
                        <option value="OTHER">Other</option>
                      </TextField>
                    </div>
                  </div>
                )
              )}

Now in the name handler handleGroupName I am doing this to get the div(to get index [from its name attribute] which stores index to push this value in main array groupDetails):

const handleGroupName = (event) => {
    let elem = document.getElementsByClassName("getIndex");  // get div 
    for (var i = 0; i < groupMembersCount; i++) {
      console.log("index is: ", elem[i].getAttribute('name')); // get the index number 
    }
  };

After doing all this I am having issues in few things: 1. One of the form fields (TextField) with gender in name attribute, I'm unable to use select to create a dropdown (writing what is shown here causes a blank screen) so I commented it out. 2. In handleGroupName handler within the for loop it shows the error as: UserDetails.jsx:434 Uncaught TypeError: Cannot read property 'getAttribute' of undefined. 3. I'm new to react functional component, not much experience in it, can anyone suggest some better ways to get values from those form fields for every render and club those as an object and store at different index in the main array, the structure of main array groupDetails defines how it should look like at the end.

Upvotes: 1

Views: 1669

Answers (1)

Khabir
Khabir

Reputation: 5854

Please try this example where I take an input that define number of member in the group. Then it shows number of input groups (name, phone and gender). After filling the info if you click on the Show button, you will see the results.

import TextField from "@material-ui/core/TextField";
import React, {useState} from "react";
import Select from "@material-ui/core/Select";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";

export default function DynamicGroupMember() {
    const [groupMembersCount, setGroupMembersCount] = useState(0);
    const [show, setShow] = useState(false);
    const [groupDetails, setGroupDetails] = useState([
        {fullName: "", phoneNo: "", gender: ""},
    ]);

    function handleChange(event, index) {
        console.log(event.target.value, index);
        let newArr = [...groupDetails]; // copying the old datas array
        let item = newArr[index];
        item = {...item, [event.target.name]: event.target.value};
        newArr[index] = item;

        setGroupDetails(newArr);
    }

    return (
        <div>
            Number of Group: <TextField name="groupMembersCount" onChange={(event) => {
            setGroupMembersCount(event.target.value)
        }}/>
            {Array.apply(null, {length: groupMembersCount}).map(
                (e, i) => (
                    <div key={i}>
                        <strong>Member #{i + 1}</strong>
                        <div className="getIndex" name={i + 1}>
                            <TextField
                                id={`name${i + 1}`}
                                name="fullName"
                                variant="outlined"
                                margin="none"
                                label="Name"
                                onChange={(event) => {
                                    handleChange(event, i)
                                }}
                            />
                            <TextField
                                id={`phoneNo${i + 1}`}
                                name="phoneNo"
                                variant="outlined"
                                margin="none"
                                label="Mobile Number"
                                onChange={(event) => {
                                    handleChange(event, i)
                                }}
                            />
                            <Select
                                id={`gender${i + 1}`}
                                name="gender"
                                variant="outlined"
                                margin="none"
                                label="Gender"
                                onChange={(event) => {
                                    handleChange(event, i)
                                }}
                            >
                                <option value="MALE">Male</option>
                                <option value="FEMALE">Female</option>
                                <option value="OTHER">Other</option>
                            </Select>
                        </div>
                    </div>
                )
            )}
            <Button onClick={() => {
                setShow(true)
            }}>Show</Button>
            {
                show ?
                    groupDetails.map(member =>
                        <Card>
                        <CardContent>
                            <Typography color="textSecondary" gutterBottom>
                                {member.fullName}
                            </Typography>
                            <Typography variant="h5" component="h2">
                                {member.phoneNo}
                            </Typography>
                            <Typography color="textSecondary">
                                {member.gender}
                            </Typography>
                        </CardContent>
                    </Card>) : null
            }
        </div>
    );
}

Upvotes: 1

Related Questions