Правильный способ использования контроллера формы реакции с автозаполнением Material-UI

Я пытаюсь использовать пользовательский компонент Material-UI Autocomplete и подключить его к react-hook-form.

TL; DR: необходимо использовать автозаполнение MUI с контроллером формы реакции без defaultValue

Мой пользовательский Autocomplete компонент принимает объект со структурой {_id:'', name: ''}, он отображает имя и возвращает _id, когда выбран параметр. Autocomplete работает нормально.

<Autocomplete
  options={options}
  getOptionLabel={option => option.name}
  getOptionSelected={(option, value) => option._id === value._id}
  onChange={(event, newValue, reason) => {
    handler(name, reason === 'clear' ? null : newValue._id);
  }}
  renderInput={params => <TextField {...params} {...inputProps} />}
/>

Чтобы заставить его работать с react-hook-form, я установил setValues как обработчик для onChange в Autocomplete и вручную зарегистрировал компонент в useEffect следующим образом

useEffect(() => {
  register({ name: "country1" });
},[]);

Это нормально работает, но я бы хотел не использовать ловушку useEffect и просто как-то напрямую использовать регистр.

Затем я попытался использовать компонент Controller из react-hook-form, чтобы правильно зарегистрировать поле в форме, а не использовать ловушку useEffect.

<Controller
  name="country2"
  as={
    <Autocomplete
      options={options}
      getOptionLabel={option => option.name}
      getOptionSelected={(option, value) => option._id === value._id}
      onChange={(event, newValue, reason) =>
        reason === "clear" ? null : newValue._id
      }
      renderInput={params => (
        <TextField {...params} label="Country" />
      )}
    />
  }
  control={control}
/>

Я изменил onChange в компоненте Autocomplete, чтобы вернуть значение напрямую, но похоже, что это не работает.

Использование inputRef={register} на <TextField/> не поможет мне, потому что я хочу сохранить _id, а не name

ЗДЕСЬ - это рабочая песочница с двумя вариантами. Первый с useEffect и setValue в Autocomplete, который работает. Вторая моя попытка использовать Controller компонент

Любая помощь приветствуется.

LE

После комментария Билла с рабочей песочницей MUI Autocomplete мне удалось получить функциональный результат

<Controller
  name="country"
  as={
    <Autocomplete
      options={options}
      getOptionLabel={option => option.name}
      getOptionSelected={(option, value) => option._id === value._id}
      renderInput={params => <TextField {...params} label="Country" />}
    />
  }
  onChange={([, { _id }]) => _id}
  control={control}
/>

Единственная проблема в том, что у меня в консоли появляется MUI Error

Material-UI: компонент изменяет состояние неконтролируемого значения автозаполнения, чтобы управлять им.

Я пытался установить для него defaultValue, но он все равно ведет себя так. Также я бы не хотел устанавливать значение по умолчанию из массива параметров из-за того, что эти поля в форме не требуются.

Обновленная песочница ЗДЕСЬ

Любая помощь по-прежнему очень ценится


person Sabbin    schedule 07.05.2020    source источник
comment
codeandbox.io/s/react-hook-form-controller-079xx ты такое видел?   -  person Bill    schedule 08.05.2020
comment
@Bill, спасибо за ссылку, я просмотрел это рабочий пример, но я все еще сталкиваюсь с некоторыми другими проблемами, связанными с состоянием компонента автозаполнения. Я обновил вопрос с помощью LE. Спасибо   -  person Sabbin    schedule 10.05.2020
comment
если вы следите за тем, что находится в списке кодов, это должно решить проблему, не так ли?   -  person Bill    schedule 10.05.2020


Ответы (3)


Итак, я исправил это. Но это выявило то, что я считаю ошибкой в ​​автозаполнении.

Во-первых ... конкретно для вашей проблемы, вы можете устранить MUI Error, добавив defaultValue к <Controller>. Но это было только начало очередного раунда или проблем.

Проблема в том, что функциям для getOptionLabel, getOptionSelected и onChange иногда передается значение (т.е. _id в данном случае), а иногда передается структура параметров - как и следовало ожидать.

Вот код, который я наконец придумал:

import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";
import { Autocomplete } from "@material-ui/lab";
import { Button } from "@material-ui/core";
export default function FormTwo({ options }) {
  const { register, handleSubmit, control } = useForm();

  const getOpObj = option => {
    if (!option._id) option = options.find(op => op._id === option);
    return option;
  };

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        name="country"
        as={
          <Autocomplete
            options={options}
            getOptionLabel={option => getOpObj(option).name}
            getOptionSelected={(option, value) => {
              return option._id === getOpObj(value)._id;
            }}
            renderInput={params => <TextField {...params} label="Country" />}
          />
        }
        onChange={([, obj]) => getOpObj(obj)._id}
        control={control}
        defaultValue={options[0]}
      />
      <Button type="submit">Submit</Button>
    </form>
  );
}
person Cliff Chaney    schedule 11.05.2020
comment
Спасибо, что нашли время ответить. Установка значения по умолчанию для меня не вариант, потому что поля не являются обязательными, поэтому они должны быть пустыми. Также я раздвоил свою песочницу и обновил ваш код. Опция очистки приводит к поломке приложения, проверьте его codesandbox.io/s/busy-forest-tmb6s - person Sabbin; 11.05.2020
comment
Обе проблемы решаются достаточно легко. Просто нужно разрешить пустой / нулевой параметр в getOpObj и getOptionSelected. Затем вы можете передать значение null в качестве параметра defaultOption. - person Cliff Chaney; 11.05.2020

Принятый ответ (вероятно) работает для версии Autocomplete с ошибками. Думаю, через некоторое время ошибка была исправлена, так что решение можно немного упростить.

Это очень полезный справочник / codeandbox при работе с response-hook-form и material-ui: https://codesandbox.io/s/react-hook-form-controller-601-j2df5?

Из приведенной выше ссылки я изменил пример автозаполнения:

import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';


const ControlledAutocomplete = ({ options = [], renderInput, getOptionLabel, onChange: ignored, control, defaultValue, name, renderOption }) => {
  return (
    <Controller
      render={({ onChange, ...props }) => (
        <Autocomplete
          options={options}
          getOptionLabel={getOptionLabel}
          renderOption={renderOption}
          renderInput={renderInput}
          onChange={(e, data) => onChange(data)}
          {...props}
        />
      )}
      onChange={([, data]) => data}
      defaultValue={defaultValue}
      name={name}
      control={control}
    />
  );
}

С использованием:

<ControlledAutocomplete
    control={control}
    name="inputName"
    options={[{ name: 'test' }]}
    getOptionLabel={(option) => `Option: ${option.name}`}
    renderInput={(params) => <TextField {...params} label="My label" margin="normal" />}
    defaultValue={null}
/>

control из возвращаемого значения useForm(}

Обратите внимание, что я передаю null как defaultValue, поскольку в моем случае этот ввод не требуется. Если вы оставите defaultValue, вы можете получить некоторые ошибки из библиотеки material-ui.

ОБНОВЛЕНИЕ:

На вопрос Стива в комментариях, вот как я визуализирую ввод, чтобы он проверял наличие ошибок:

renderInput={(params) => (
                  <TextField
                    {...params}
                    label="Field Label"
                    margin="normal"
                    error={errors[fieldName]}
                  />
                )}

Где errors - объект из formMethods react-hook-form:

const { control, watch, errors, handleSubmit } = formMethods
person krinoid    schedule 11.09.2020
comment
Мне любопытно, как вы обрабатываете требуемые поля автозаполнения - для меня с рендерингом это не вызывает ошибок - person Steve; 22.01.2021
comment
@Steve - Я вижу несколько причин, по которым ошибки не появляются, в зависимости от: библиотеки проверки и схемы проверки, структуры компонентов, наименования полей (например, вам нужно использовать индексы в имени поля, если вы используете поле массива) . Если вы предоставите более подробную информацию о своем деле, я могу адаптировать ответ. В моем случае я использую yup для проверки, поэтому поле имеет .required() в своей схеме. Я также добавлю быстрый пример того, как я визуализирую ввод, чтобы отображалась эта ошибка. Дайте мне знать, если это поможет. - person krinoid; 23.01.2021

Вместо использования контроллера с помощью register, setValue of useForm и value, onChange of Autocomplete мы можем добиться того же результата.

const [selectedCaste, setSelectedCaste] = useState([]);
const {register, errors, setValue} = useForm();

useEffect(() => {
  register("caste");
}, [register]);

return (
                <Autocomplete
                  multiple
                  options={casteList}
                  disableCloseOnSelect
                  value={selectedCaste}
                  onChange={(_, values) => {
                    setSelectedCaste([...values]);
                    setValue("caste", [...values]);
                  }}
                  getOptionLabel={(option) => option}
                  renderOption={(option, { selected }) => (
                    <React.Fragment>
                      <Checkbox
                        icon={icon}
                        checkedIcon={checkedIcon}
                        style={{ marginRight: 8 }}
                        checked={selected}
                      />
                      {option}
                    </React.Fragment>
                  )}
                  style={{ width: "100%" }}
                  renderInput={(params) => (
                    <TextField
                      {...params}
                      id="caste"
                      error={!!errors.caste}
                      helperText={errors.caste?.message}
                      variant="outlined"
                      label="Select caste"
                      placeholder="Caste"
                    />
                  )}
                />
);
person Shailendra    schedule 21.05.2021
comment
Можно, но мы меняем сложность компонента ... - person Sabbin; 26.05.2021
comment
Это кажется более простым, но упомянутый выше метод позволит сделать его гораздо более масштабируемым и более простым для повторного использования в приложении. Поскольку компонент Control определен в специальном файле ControlledAutoComplete, мы можем импортировать его в любую форму в приложении и просто передать элемент управления и определенные параметры формы из файла этой формы. Это особенно удобно, если вызов api в файле форм извлекает параметры для автозаполнения. - person Timothy Wilburn; 28.05.2021