20

我正在尝试使用自定义Material-UI Autocomplete组件并将其连接到react-hook-form.

TLDR:需要在没有 react-hook-form 控制器的情况下使用 MUI AutocompletedefaultValue

我的自定义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我将 设置setValuesonChangein 的处理程序Autocomplete并手动注册组件,useEffect如下所示

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

这工作正常,但我不想有useEffect钩子,只是直接以某种方式使用寄存器。

接下来我尝试使用Controller组件 fromreact-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}on<TextField/>不会为我削减它,因为我想保存_id而不是name

这里是一个包含两种情况的工作沙箱。第一个 withuseEffectsetValueinAutocomplete有效。我使用Controller组件的第二次尝试

任何帮助表示赞赏。

在比尔用 MUI 自动完成的工作沙箱发表评论后,我设法得到了一个功能结果

<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:一个组件正在将 Autocomplete 的非受控值状态更改为被控制。

我试图defaultValue为它设置一个,但它仍然表现得像那样。此外,由于不需要表单中的这些字段,我也不想从选项数组中设置默认值。

更新的沙盒在这里

任何帮助仍然非常感谢

4

4 回答 4

18

接受的答案(可能)适用于自动完成的错误版本。我认为该错误已在一段时间后修复,因此可以稍微简化解决方案。

在使用 react-hook-form 和 material-ui 时,这是非常有用的参考/codesandbox: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(}

请注意,我正在传递nulldefaultValue因为在我的情况下,不需要此输入。如果你离开defaultValue,你可能会从 material-ui 库中得到一些错误。

更新:

根据评论中的史蒂夫问题,这就是我呈现输入的方式,以便它检查错误:

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

'serrors的对象在哪里:react-hook-formformMethods

const { control, watch, errors, handleSubmit } = formMethods
于 2020-09-11T19:18:29.070 回答
12

所以,我解决了这个问题。但它揭示了我认为自动完成的错误。

首先...特别针对您的问题,您可以MUI Error通过<Controller>. 但这只是另一轮或问题的开始。

问题是函数getOptionLabel,getOptionSelectedonChange有时会传递值(即_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>
  );
}
于 2020-05-11T03:43:04.663 回答
4
import { Button } from "@material-ui/core";
import Autocomplete from "@material-ui/core/Autocomplete";
import { red } from "@material-ui/core/colors";
import Container from "@material-ui/core/Container";
import CssBaseline from "@material-ui/core/CssBaseline";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import AdapterDateFns from "@material-ui/lab/AdapterDateFns";
import LocalizationProvider from "@material-ui/lab/LocalizationProvider";
import React, { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";


export default function App() {
  const [itemList, setItemList] = useState([]);
  // const classes = useStyles();

  const {
    control,
    handleSubmit,
    setValue,
    formState: { errors }
  } = useForm({
    mode: "onChange",
    defaultValues: { item: null }
  });

  const onSubmit = (formInputs) => {
    console.log("formInputs", formInputs);
  };

  useEffect(() => {
    setItemList([
      { id: 1, name: "item1" },
      { id: 2, name: "item2" }
    ]);
    setValue("item", { id: 3, name: "item3" });
  }, [setValue]);

  return (
    <LocalizationProvider dateAdapter={AdapterDateFns}>
      <Container component="main" maxWidth="xs">
        <CssBaseline />

        <form onSubmit={handleSubmit(onSubmit)} noValidate>
          <Controller
            control={control}
            name="item"
            rules={{ required: true }}
            render={({ field: { onChange, value } }) => (
              <Autocomplete
                onChange={(event, item) => {
                  onChange(item);
                }}
                value={value}
                options={itemList}
                getOptionLabel={(item) => (item.name ? item.name : "")}
                getOptionSelected={(option, value) =>
                  value === undefined || value === "" || option.id === value.id
                }
                renderInput={(params) => (
                  <TextField
                    {...params}
                    label="items"
                    margin="normal"
                    variant="outlined"
                    error={!!errors.item}
                    helperText={errors.item && "item required"}
                    required
                  />
                )}
              />
            )}
          />

          <button
            onClick={() => {
              setValue("item", { id: 1, name: "item1" });
            }}
          >
            setValue
          </button>

          <Button
            type="submit"
            fullWidth
            size="large"
            variant="contained"
            color="primary"
            // className={classes.submit}
          >
            submit
          </Button>
        </form>
      </Container>
    </LocalizationProvider>
  );
}
于 2021-08-19T06:45:08.030 回答
-3

不使用控制器,借助 register、setValue 的 useForm 和 value、onChange 的 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"
                    />
                  )}
                />
);
于 2021-05-21T20:45:57.003 回答