简体   繁体   中英

Material UI autocomplete with react-form-hook validation value is not changing properly

I am trying to implement multi-select Mui autocomplete.

Whenever user selects an option, I want Chip component to be displayed underneath.

I am using react-form-hook to check validation. categories field is an array, and I want it to have at least one item.

Problem is when I delete a chip, the value does not update properly. I am using react state to keep track of selectedItems so I can display Chip component.

When I delete the chip, it is deleted from selectedItem state, but actual value from Controller does not change.

Please review my code, and give me some feedbacks. Thank you all!!

mui_autocomplete

import React, { useEffect, useState } from 'react';
import { Autocomplete, Chip, FormControl, FormLabel, Stack, TextField } from '@mui/material';
import { Controller } from 'react-hook-form';
import CloseIcon from '@mui/icons-material/Close';

export default function FormAutoComplete({ name, control, label, error, ...props }) {
  const [selectedItems, setSelectedItems] = useState([]);

  const selectedItemChip = selectedItems.map((item) => {
    return (
      <Chip
        key={item}
        label={item}
        deleteIcon={<CloseIcon />}
        onDelete={() => {
          setSelectedItems((prev) => prev.filter((entry) => entry !== item));
        }}
      />
    );
  });

  return (
    <FormControl fullWidth>
      <FormLabel>{label}</FormLabel>
      <Controller
        name={name}
        control={control}
        render={({ field: { onChange, value } }) => (
          <Autocomplete
            multiple
            filterSelectedOptions
            options={options}
            getOptionLabel={(option) => option}
            renderTags={() => {}}
            value={selectedItems}
            onChange={(e, newValue) => {
              const addedItem = newValue[newValue.length - 1];
              setSelectedItems((prev) => [...prev, addedItem]);
              onChange(selectedItems);
              return selectedItems;
            }}
            renderInput={(params) => (
              <TextField
                {...params}
                {...props}
                error={!!error}
                helperText={error && error.message}
              />
            )}
          />
        )}
      />
      <Stack direction="row" marginTop={2} gap={1} flexWrap="wrap">
        {selectedItemChip}
      </Stack>
    </FormControl>
  );
}

export const options = [
  'Building Materials',
  'Tools',
  'Decor & Furniture',
  'Bath',
  'Doors & Windows',
  'Cleaning',
  'Electrical',
  'Heating & Cooling',
  'Plumbing',
  'Hardware',
  'Kitchen',
  'Lawn & Garden',
  'Lighting & Fans',
];

Sandbox

Once chip is deleted, react-hook-form is not updated with latest value. So, when form is submitted, old data is logged.

  1. No need to track selectedItems in a state variable. You can use useWatch hook to get latest value from react-hook-form
  2. use setValue to update value in formState

With these changes your FormAutoComplete component should look like this

export default function FormAutoComplete({
  name,
  control,
  label,
  error,
  setValue, // Pass this prop from parent component. Can be destructured from useForm hook
  ...props
}) {
  // const [selectedItems, setSelectedItems] = useState([]);
  const selectedItems = useWatch({control,name}) // import useWatch from react-hook-form
  const selectedItemChip = selectedItems.map((item) => {
    return (
      <Chip
        key={item}
        label={item}
        deleteIcon={<CloseIcon />}
        onDelete={() => {
          // setSelectedItems((prev) => [...prev.filter((entry) => entry !== item)]);
        setValue(name,selectedItems.filter((entry) => entry !== item))
        }}
      />
    );
  });

  return (
    <FormControl fullWidth>
      <FormLabel>{label}</FormLabel>
      <Controller
        name={name}
        control={control}
        render={({ field: { onChange, value } }) => (
          <Autocomplete
            multiple
            filterSelectedOptions
            options={options}
            getOptionLabel={(option) => option}
            renderTags={() => {}}
           // value={selectedItems}
            value={value}
            onChange={(e, newValue) => {
              // setSelectedItems(newValue);
              onChange(newValue);
            }}
            renderInput={(params) => (
              <TextField
                {...params}
                {...props}
                error={!!error}
                helperText={error && error.message}
              />
            )}
          />
        )}
      />
      <Stack direction="row" marginTop={2} gap={1} flexWrap="wrap">
        {selectedItemChip}
      </Stack>
    </FormControl>
  );
}

Documentation :

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