簡體   English   中英

支持在使用 React Hook 表單驗證時更改另一個字段值的回調

[英]Support callback for changing another field value when using React Hook Form validation

長話短說

這有效: https://codesandbox.io/s/stoic-beaver-ucydi

使用 React Hook Form 重構后,這不起作用: https://codesandbox.io/s/objective-cloud-bkunr?file=/src/ControlledTextField.tsx


很長的故事

沒有 React Hook Form(工作正常)

我最近使用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>

關於 state 我只留下了值復制需要的東西:

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

問題

我無法同時進行 React Hook 表單驗證和我的值復制功能。

使用此代碼時,驗證效果很好但用戶無法編輯 Site URL 字段:

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

或復制和編輯字段值效果很好,但即使輸入了值,驗證也會說兩個字段都是空的(必填),當使用此代碼時:

onChange={onChangeCallback}
value={refValue}

好的,我明白了。

而不是使用 state 來更新字段值

  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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM