简体   繁体   中英

How do I create dynamic select fields?

I have these 2 fields size and design in which the user can add more of these 2 fields as many times as they want.

Example:

I selected M for the size. It does show in the console:

在此处输入图像描述

Additionally, why is it rendering two of size and design at the first load of the screen? Also, add

And now selecting a design:

It will remove the value that was previously selected in the size field.

在此处输入图像描述

And in the console, the value of size has been replaced with design2

在此处输入图像描述

codesandbox link: https://codesandbox.io/s/form-1-ls6rx?file=/demo.js

import React, { useState, useEffect } from "react";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";

import { TextField, Button } from "@mui/material";

export default function BasicSelect() {
  const [prod, setProd] = useState("");
  const [qty, setQty] = useState(0);
  const [design, setDesign] = useState("");
  const [sizeList, setSizeList] = useState([{ size: "", design: "" }]);

  const handleChange = (event) => {
    setProd(event.target.value);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    console.log(prod, qty, sizeList, design);
  };

  //helper method
  const handleAdd = () => {
    setSizeList([...sizeList, { size: "", design: "" }]);
  };

  const handleRemove = (index) => {
    const list = [...sizeList];
    list.splice(index, 1);
    setSizeList(list);
  };

  const handleSizeChange = (e, index) => {
    const { value } = e.target;

    setSizeList((prev) =>
      Object.assign([...prev], {
        [index]: { size: value }
      })
    );
  };

  useEffect(() => {
    console.log(sizeList);
  }, [sizeList]);

  return (
    <Box sx={{ minWidth: 120 }}>
      <form onSubmit={handleSubmit}>
        <FormControl fullWidth>
          <InputLabel id="demo-simple-select-label">Product</InputLabel>
          <Select
            labelId="demo-simple-select-label"
            id="demo-simple-select"
            value={prod}
            label="Product"
            onChange={handleChange}
          >
            <MenuItem value="Item1">Item1</MenuItem>
            <MenuItem value="Item2">Item2</MenuItem>
            <MenuItem value="Item3">Item3</MenuItem>
          </Select>
        </FormControl>
        <br />
        <br />

        <br />

        <br />
        {sizeList.map((singleSize, index) => (
          <div key={index}>
            <FormControl fullWidth>
              <InputLabel id="demo-simple-select-label">Size</InputLabel>
              <Select
                labelId="demo-simple-select-label"
                id="size"
                value={singleSize.size}
                label="Product"
                onChange={(e) => handleSizeChange(e, index)}
              >
                <MenuItem value="S">Small</MenuItem>
                <MenuItem value="M">Medium</MenuItem>
                <MenuItem value="L">Large</MenuItem>
              </Select>
            </FormControl>

            <FormControl fullWidth>
              <InputLabel id="demo-simple-select-label">
                Choose Design
              </InputLabel>
              <Select
                labelId="demo-simple-select-label"
                id="design"
                value={singleSize.design}
                label="Product"
                onChange={(e) => handleSizeChange(e, index)}
              >
                <MenuItem value="Design1">Design1</MenuItem>
                <MenuItem value="Design2">Design2</MenuItem>
                <MenuItem value="Design3">Design3</MenuItem>
              </Select>
            </FormControl>

            <br />
            <br />
            {sizeList.length > 1 && (
              <Button
                onClick={() => handleRemove(index)}
                variant="contained"
                color="secondary"
              >
                Remove{" "}
              </Button>
            )}
            <br />
            <br />
            {sizeList.length - 1 === index && (
              <Button variant="contained" onClick={handleAdd}>
                {" "}
                Add Quantity
              </Button>
            )}
          </div>
        ))}

        <br />
        <br />

        <br />
        <br />
        <Button type="submit">Submit </Button>
      </form>
      <Button>Add more Product </Button>
    </Box>
  );
}

You are using the same handler that is supposed to handle and update states for both the design and size, also there lies a problem in how you are updating the state using Object.assign , this is also leading to additional warnings in the console regarding the value passed, the issue is most likely due to the event conflict. To put things in place, simply use different handlers to handle updates of different object attributes. A simple solution is to create a new array, make the necessary updates and set the new array to be the updated state, I tested this and it works as expected.

const handleSizeChange = (e, index) => {
    const { value } = e.target;
    console.log(value)
    const arr = [...sizeList] //Shallow copy the existing state
    arr[index].size = value //Update the size to the selected size
    
    setSizeList([...arr]); //Set the updated array to be the new state
};

Add a new handler for updating the design value.

const handleDesignChange = (e,index)=>{
    const { value } = e.target;
    console.log(value)
    const arr = [...sizeList]
    arr[index].design = value
    // console.log(arr)
    setSizeList([...arr]);
}

Alternatively, you could club both the handlers into a single handler by adding conditional checks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM