TL;博士
这有效:https ://codesandbox.io/s/stoic-beaver-ucydi
使用 React Hook Form 重构后,这不起作用:https ://codesandbox.io/s/objective-cloud-bkunr?file=/src/ControlledTextField.tsx
很长的故事
没有 React Hook 表单(工作正常)
我最近使用Fluent UI构建了一个有状态的 React 表单,并在自定义组件中包装了字段。
我已经包含了一个功能,其中站点 URL 字段中的值是在您在站点标题字段中键入时生成的(它只是复制字段值并删除在我的情况下对 URL 无效的字符)。
(简化的)代码运行良好,看起来像这样:
import * as React from 'react';
import {useState} from 'react';
import { PrimaryButton } from 'office-ui-fabric-react';
import SiteTitleField from '../../../common/formFields/SiteTitleField';
import SiteUrlField from '../../../common/formFields/SiteUrlField';
export default function MyForm(props) {
const urlPrefix: string = "https://" + window.location.hostname + "/sites/";
const [siteTitle, setSiteTitle] = useState();
const [titleErrorMessage, setTitleErrorMessage] = useState('');
const [siteUrl, setsiteUrl] = useState();
const [urlErrorMessage, setUrlErrorMessage] = useState('');
function handleTitleChange(e) {
if (e.target.value.length) {
setTitleErrorMessage('');
} else {
setTitleErrorMessage('This field is required.');
}
setSiteTitle(e.target.value);
setsiteUrl(e.target.value.replace(/[^A-Za-z0-9_-]/g, ""));
}
function handleUrlChange(e) {
if (e.target.value.length) {
setUrlErrorMessage('');
} else {
setUrlErrorMessage('This field is required.');
}
setsiteUrl(e.target.value);
}
function handleButtonClick(e) {
// call to API
}
return (
<SiteTitleField
siteTitle={siteTitle}
titleErrorMessage={titleErrorMessage}
handleTitleChange={handleTitleChange}
/>
<SiteUrlField
siteUrl={siteUrl}
urlErrorMessage={urlErrorMessage}
urlPrefix={urlPrefix}
handleUrlChange={handleUrlChange}
/>
<PrimaryButton
text="Create a Request"
onClick={handleButtonClick}
/>
);
}
SiteTitleField 组件:
import * as React from 'react';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
export default function SiteTitleField(props) {
return (
<TextField
value={props.siteTitle}
required
aria-required="true"
errorMessage={props.titleErrorMessage}
label="Site Title"
placeholder="Set the title of the site"
onChange={props.handleTitleChange}
/>
);
}
SiteUrlField 组件:
import * as React from 'react';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
export default function SiteUrlField(props) {
return (
<TextField
value={props.siteUrl}
required
aria-required="true"
errorMessage={props.urlErrorMessage}
label="Site URL"
prefix={props.urlPrefix}
placeholder="Set site URL alias"
onChange={props.handleUrlChange}
/>
);
}
使用 React Hook Form(无法正常工作)
现在我正在尝试使用 React Hook Form 和 Yup 验证模式重构我的表单。
我已经用 React Hook Form Controller 组件及其渲染属性包装了 Fluent UI TextField 组件:
import * as React from 'react';
import { Control, Controller, FieldErrors } from "react-hook-form";
import { TextField } from 'office-ui-fabric-react';
export interface IControlledTextFieldProps {
control: Control<any>;
name: string;
errors: FieldErrors<any>;
label?: string;
prefix?: string;
placeholder?: string;
onChangeCallback?: (...event: any[]) => void;
refValue?: string;
}
export const ControlledTextField: React.FC<IControlledTextFieldProps> = ({
control,
name,
errors,
label,
prefix,
placeholder,
onChangeCallback,
refValue,
}) => {
return (
<Controller
name={name}
control={control}
disabled={disabled}
render={({ onChange, onBlur, value, name: fieldName }) => (
<TextField
onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {onChange(refValue); onChangeCallback && onChangeCallback(event);}}
value={refValue}
onBlur={onBlur}
name={fieldName}
errorMessage={errors[fieldName] && errors[fieldName].message}
label={label}
prefix={prefix}
placeholder={placeholder}
/>
)}
/>
);
};
我已经相应地替换了 SiteTitleField 和 SiteUrlField 的代码,并添加了一个简单的 Yup 验证模式:
const schema = yup.object().shape({
siteTitle: yup.string().required("Site Title needs to be provided."),
siteUrl: yup.string().required("Site URL needs to be provided."),
});
const { handleSubmit, errors, control } = useForm<Inputs>({
resolver: yupResolver(schema)
});
我已经用<form>
标签包装了表单并相应地更改了字段属性:
<form onSubmit={handleSubmit(handleButtonClick)}>
<SiteTitleField
name="siteTitle"
control={control}
errors={errors}
handleTitleChange={handleTitleChange}
/>
<SiteUrlField
name="siteUrl"
control={control}
errors={errors}
siteUrl={siteUrl}
urlPrefix={urlPrefix}
/>
<PrimaryButton
text="Create a Request"
type="submit"
/>
</form>
关于状态,我只留下了值复制所需的东西:
const [siteUrl, setsiteUrl] = useState();
function handleTitleChange(e) {
setsiteUrl(e.target.value.replace(/[^A-Za-z0-9_-]/g, ""));
}
问题
我无法让 React Hook Form 验证和我的值复制功能同时工作。
使用此代码时,验证效果很好,但用户无法编辑站点 URL 字段:
onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {onChange(refValue); onChangeCallback && onChangeCallback(event);}}
value={refValue}
或复制和编辑字段值效果很好,但即使输入的值验证表明两个字段都是空的(必需),使用此代码时:
onChange={onChangeCallback}
value={refValue}