reactjs - 支持使用 React Hook 表单验证时更改另一个字段值的回调

标签 reactjs react-hooks yup react-hook-form fluentui-react

TL;DR

这工作: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 表单(无法正常工作)

现在我正在尝试使用 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 表单验证和我的值复制功能同时工作。

使用此代码时,验证效果很好,但用户无法编辑站点 URL 字段:

onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {onChange(refValue); onChangeCallback && onChangeCallback(event);}}
value={refValue}

或复制和编辑字段值效果很好,但即使输入的值验证表明两个字段都是空的(必需),使用此代码时:

onChange={onChangeCallback}
value={refValue}

最佳答案

好的,我想通了。

而不是使用状态来更新字段值

  const [siteUrl, setsiteUrl] = useState();
  
  function handleTitleChange(e) {
    setsiteUrl(e.target.value.replace(/[^A-Za-z0-9_-]/g, ""));
  }

我应该使用 useFormsetValue :

  const { 
    handleSubmit, 
    errors, 
    control, 
    setValue // added
  } = 
  useForm<Inputs>({
    resolver: yupResolver(schema)
  });

  function handleTitleChange(e) {
    // changed:
    setValue("siteUrl", e.target.value.replace(/[^A-Za-z0-9_-]/g, ""), {
      shouldValidate: true
    });
  }

ControlledTextField中的value应该简单解析为:

value={value}

工作解决方案: https://codesandbox.io/s/focused-montalcini-ehbp3?file=/src/App.tsx

关于reactjs - 支持使用 React Hook 表单验证时更改另一个字段值的回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65100499/

相关文章:

reactjs - 在空输入提交时显示值类型错误消息而不是必需的错误消息

jestjs - Formik,开 Jest ,是的 : how to test validation?

regex - 是的,验证密码至少包含一个特殊字符

javascript - 语法错误: Unexpected token equal sign '='

reactjs - 防止事件监听器多次触发

javascript - React JS - 如何在更新状态 [Hooks] 之前防止渲染

javascript - 将 Hooks 与 Redux 一起使用——不好的做法?

reactjs - 错误 : Cannot find module 'eslint-config-flowtype'

javascript - 当 prop 是函数时,测试子组件 props 是否正确构造

reactjs - React Hooks - 如何测试全局提供者的更改