如果输入在 material ui 对话框中,则 react-hook-form 的 setValue 方法不起作用

react-hook-form's setValue method is not working if input is in material ui dialog

我尝试使用 react-hook-form 来验证输入。但是我发现如果输入放在 Material UI 的对话框组件中,react-hook-form 的 setValue 没有按预期工作,但是当我删除对话框组件时它可以工作。估计是组件挂载前设置了值,但还是找不到解决方法

将从服务器获取值,所以我不能使用 react-hook-form 的 defaultValues

https://codesandbox.io/s/react-hook-form-material-ui-twbbw

我试过用useState来控制输入值,但是还有一个问题。清除输入后,点击提交按钮,出现错误信息,我输入的第一个字母不会显示。

https://codesandbox.io/s/react-hook-form-material-ui-ve2en

对于外部控制组件

如果您使用的是 V3,我建议您使用 react-hook-form-input https://github.com/react-hook-form/react-hook-form-input

import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
];

function App() {
  const { handleSubmit, register, setValue, reset } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <RHFInput
        as={<Select options={options} />}
        rules={{ required: true }}
        name="reactSelect"
        register={register}
        setValue={setValue}
      />
      <button type="button">Reset Form</button>
      <button>submit</button>
    </form>
  );
}

如果您使用的是 V4,我建议您使用 Controller https://react-hook-form.com/api/#Controller

import React from 'react';
import Select from 'react-select';
import { TextField } from "@material-ui/core";
import { useForm, Controller } from 'react-hook-form';

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
];

function App() {
  const { handleSubmit, control } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        as={<Select options={options} />}
        control={control}
        rules={{ required: true }}
        onChange={([selected]) => {
          // React Select return object instead of value for selection
          return { value: selected };
        }}
        name="reactSelect"
      />

      <Controller
        as={<TextField />}
        name="firstName"
        control={control}
      />

      <button>submit</button>
    </form>
  );
}

包装受控组件并在其中收集数据的想法,同时仍然在外部受控组件内隔离重新渲染。

问题出在寄存器函数上。在调用 Textfield 的 ref 后,您正在使用 register 注册 Textfield。

在初始渲染后调用 useEffect 将名称设置为 setValue 123。如果 open 为真,则对话框内容在 useEffect 之后呈现。 内容渲染完成后,调用带寄存器的ref,将Textfield(这里undefined)的默认值设置为name.

的值

这就是文本字段的值显示为“”的原因。调用render和ref回调后需要调用setValue,让值持久化。

您有两种选择:

  1. open 更改后,使用异步延迟(setTimeout 或承诺)在 useEffect 中设置异步值。因此,如果您将 open 添加到 useEffect 依赖数组并设置值 async,它就可以工作。这是一个Sandbox.
  2. 设置文本字段的默认值或使用 useForm({defaultValues: {name: '123}}) 将默认值添加到挂钩。

由于 setValue 有其特殊性,正如 @Domino987 在上面所解释的那样,对于表单填充从服务器获取的数据的那些场景,替代方案是:

  • 使用useState保存获取的值;
  • Controllers defaultValue设置值;
  • 有条件呈现的表单

一个伪例子:


const [state, setState] = useState({name: '', requested: false});

useEffect(() => {
    HTTP_Service.getName().then(name => {
      setCompanyInfo({name, requested: true})
    });

}, []);

const {name, requested} = state

return ({requested ? <Text>Loading...</Text> : <View>
    <Controller
        as={
        <Input               
          label={t('name')}
          placeholder={t('name')}
        />
        }
        defaultValue={name}
        control={control}
        name="name"
        onChange={args => args[0].nativeEvent.text}
    />
</View>});

react-hook-form with controlled input, yup validation, material UI component, setValue

    import React from 'react';
    import {useForm, Controller} from 'react-hook-form';
    import {yupResolver} from '@hookform/resolvers/yup';
    import Autocomplete from '@material-ui/lab/Autocomplete';
    import { TextField } from '@material-ui/core';
    import * as yup from 'yup';
    
    const schema = yup.object().shape({
      firstname: yup.string().required(),
      buyer: yup.string().required(),
    });
    
    const UserForm = () => {
      const {
        watch,
        setValue,
        register,
        handleSubmit,
        control,
        formState: {errors},
      } = useForm({
        defaultValues: {
          item: {"id":2,"name":"item2"},
        },
        resolver: yupResolver(schema),
      });
    
      const itemList = [
        {id: 1, name: 'item1'},
        {id: 2, name: 'item2'},
      ];
      return (
        <div
          style={{
            margin: '200px',
          }}>
          <form onSubmit={handleSubmit(d => console.table(d))}>
          
          
            <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'}
                      
                    />
                  )}
                />
              )}
            />
    
            <input type="submit" />
            <button
              onClick={() => {
              
                setValue('item', {id: 2, name: 'item2'});
              }}>
              setValue
            </button>
            <h6>data from register</h6>
            {<pre>{JSON.stringify(watch())}</pre>}
          </form>
        </div>
      );
    };
    
    export default UserForm;

就我而言,我在模态中使用 setValue。它没有按预期工作。我必须在 useForm.

中添加 shouldUnregister: false
const { errors, register, setValue, handleSubmit } = useForm({
resolver: useYupValidationResolver(CustomDomainSchema),
mode: 'all',
shouldUnregister: false,
});

我读完这篇 Discussion on react-hook-form. Here is a working example on sandbox 后解决了这个问题。