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

Posted

技术标签:

【中文标题】如果输入在材料 ui 对话框中,react-hook-form 的 setValue 方法不起作用【英文标题】:react-hook-form's setValue method is not working if input is in material ui dialog 【发布时间】:2020-04-18 22:58:30 【问题描述】:

我尝试使用 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

【问题讨论】:

您可以简单地将其作为默认值添加到 Textfield 或使用 useForm(defaultValues: name: 123) 的表单 谢谢。似乎可行,但数据将从服务器获取,我不太确定在创建后更新defaultValues 是否是好方法。 codesandbox.io/s/react-hook-form-material-ui-viq6q 为什么不直接将默认值设置为文本字段。所以你不必更新钩子对象。这是因为 useEffect 在调用寄存器之前更新,并且在注册期间 Textfield 设置为“”(挂钩的默认值)。如果您在显示对话框后调用 setValue(异步,您已经在做),它可以工作。 setTimeout(() => (setValue("name", 123)), 1000); 您可以使用 useEffect 挂钩来设置来自服务器的值 【参考方案1】:

问题在于注册功能。在调用 Textfield 的 ref 之后,您正在使用 register 注册 Textfield。

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

这就是为什么显示的 Textfield 的值是 ""。需要在调用 render 和 ref 回调后调用 setValue,使值保持不变。

您有两种选择:

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

【讨论】:

对话框组件会被复用,每次输入的默认值可能不同。如果我使用useForm(defaultValues: name: prop.value),默认值只是第一次设置,如果prop.value不同,我必须使用setValue更新默认值。你提到的第一个选项适合我的情况,谢谢!!【参考方案2】:

对于外部受控组件

如果您使用的是 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>
  );

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

【讨论】:

感谢您的详细解释。我正在使用 V4,我曾尝试使用 Controller 来包装受控组件。但是如果表单放在 Material UI 的 Dialog 组件中,setValue 仍然无法正常工作。 codesandbox.io/s/react-hook-form-material-ui-7hcne @Sam 请提供更多关于仍然无法工作的详细信息 在示例中,我希望我使用setValue 来设置输入值“123”。但是我点击打开按钮,输入还是空的。问题应该是Dialog组件引起的,如果我删除该组件,它会正常工作。 我认为问题在于何时调用 setValue,您可能应该在对话打开后调用它 谢谢,这是个不错的选择。在 Dialog onEnter 事件中调用 setValue 以确保组件已安装【参考方案3】:

由于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>);

【讨论】:

【参考方案4】:

react-hook-form 带有受控输入、是的验证、材质 UI 组件、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;

【讨论】:

以上是关于如果输入在材料 ui 对话框中,react-hook-form 的 setValue 方法不起作用的主要内容,如果未能解决你的问题,请参考以下文章

材料ui的自定义输入中的Redux表单不起作用

React 钩子形式 - 对话框内的字段数组(材料 UI)

单击行材料-ui DataGrid内的按钮时如何设置行数据?

如何呈现材料 UI 输入控件而不是材料 UI 日期选择器的文本字段

如何将上传图标修复为文件上传输入(材料 UI)

readOnly 未在 react js 中申请材料 ui 的 KeyboardDatePicker 的输入字段