如果输入在 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
来控制输入值,但是还有一个问题。清除输入后,点击提交按钮,出现错误信息,我输入的第一个字母不会显示。
对于外部控制组件
如果您使用的是 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,让值持久化。
您有两种选择:
- 在
open
更改后,使用异步延迟(setTimeout
或承诺)在 useEffect 中设置异步值。因此,如果您将 open
添加到 useEffect 依赖数组并设置值 async,它就可以工作。这是一个Sandbox.
- 设置文本字段的默认值或使用
useForm({defaultValues: {name: '123}})
将默认值添加到挂钩。
由于 setValue
有其特殊性,正如 @Domino987 在上面所解释的那样,对于表单填充从服务器获取的数据的那些场景,替代方案是:
- 使用
useState
保存获取的值;
Controller
s 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 后解决了这个问题。
我尝试使用 react-hook-form 来验证输入。但是我发现如果输入放在 Material UI 的对话框组件中,react-hook-form 的 setValue
没有按预期工作,但是当我删除对话框组件时它可以工作。估计是组件挂载前设置了值,但还是找不到解决方法
将从服务器获取值,所以我不能使用 react-hook-form 的 defaultValues
。
https://codesandbox.io/s/react-hook-form-material-ui-twbbw
我试过用useState
来控制输入值,但是还有一个问题。清除输入后,点击提交按钮,出现错误信息,我输入的第一个字母不会显示。
对于外部控制组件
如果您使用的是 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,让值持久化。
您有两种选择:
- 在
open
更改后,使用异步延迟(setTimeout
或承诺)在 useEffect 中设置异步值。因此,如果您将open
添加到 useEffect 依赖数组并设置值 async,它就可以工作。这是一个Sandbox. - 设置文本字段的默认值或使用
useForm({defaultValues: {name: '123}})
将默认值添加到挂钩。
由于 setValue
有其特殊性,正如 @Domino987 在上面所解释的那样,对于表单填充从服务器获取的数据的那些场景,替代方案是:
- 使用
useState
保存获取的值; Controller
sdefaultValue
设置值;- 有条件呈现的表单。
一个伪例子:
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 后解决了这个问题。