Skip to content

API.V6

专注于通过简化API提供最佳开发体验

React Hook Form V7 is released. If you are planning to upgrade, please read through the Migration Guide to V7.

useForm: Function

useForm也接受可选参数。 下面的示例展示了所有选项的默认值。

const { register } = useForm({
  mode: 'onSubmit',
  reValidateMode: 'onChange',
  defaultValues: {},
  resolver: undefined,
  context: undefined,
  criteriaMode: "firstError",
  shouldFocusError: true,
  shouldUnregister: true,
})type FormInputs = {
  firstName: string;
  lastName: string;
};

const { register } = useForm<FormInputs>({
  mode: 'onSubmit',
  reValidateMode: 'onChange',
  defaultValues: {},
  resolver: undefined,
  context: undefined,
  criteriaMode: "firstError",
  shouldFocusError: true,
  shouldUnregister: true,
})
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'React Native: compatible with Controller
名字类型説明
onSubmit (Default)string校验将在submit事件 上触发,无效的输入将附加onChange事件侦听器以重新校验。
onBlurstring校验将在blur事件上触发。
onChangestring校验将在每个输入组件的change事件上触发,会导致大量的重渲染。警告:这样通常会对性能造成影响。
onTouchedstring直到输入被触动,才会触发验证。
allstring校验将在blurchange事件上触发。
reValidateMode: onChange | onBlur | onSubmit = 'onChange'React Native: Custom register or using Controller

此选项允许您配置提交后有错误的输入组件重新校验的场景(默认情况下,在输入组件变化时触发校验)。

defaultValues: Record<string, any> = {}Video
  • 您可以使用defaultValue/defaultChecked设置输入组件的默认值(从React文档中了解默认值的更多内容),或者将defaultValues作为可选参数来填充整个表单的默认值。

  • 重要提示:defaultValues将在首次渲染时缓存在自定义hook中,要重置defaultValues,请使用api。

  • 注意:defaultValues中定义的值将如defaultValue一样注入

  • 手动注册的输入组件不会自动填充(例如:register('test')),因为React Hook Form没有自定义register表单项的ref

  • 它并不是表单的默认状态, 要包含其他的表单值,可以这样做:

    1. 注册隐藏的输入组件: <input type="hidden" ref={register} name="test" />

    2. 在提交回调里组合所有的值.

const { register } = useForm({
  defaultValues: {
    firstName: "bill",
    lastName: "luo",
    email: "bluebill1049@hotmail.com",
    isDeveloper: true
  }
})

<input name="firstName" ref={register} /> // ✅ working version
<input name="lastName" ref={() => register({ name: 'lastName' })} />
// ❌ above example does not work with "defaultValues" due to its "ref" not being provided
type Inputs = {
  firstName: string;
  lastName: string;
  email: string;
  isDeveloper: boolean;
}
  
const { register } = useForm<Inputs>({
  defaultValues: {
    firstName: "bill",
    lastName: "luo",
    email: "bluebill1049@hotmail.com",
    isDeveloper: true
  }
})

<input name="firstName" ref={register} /> // ✅ working version
<input name="lastName" ref={() => register({ name: 'lastName' })} />
// ❌ above example does not work with "defaultValues" due to its "ref" not being provided
resolver: (values: any, context?: object) => Promise<ResolverResult> | ResolverResult

该函数允许使用外部校验库,例如Yup, JoiSuperstruct以及其他的。我们的目标是确保用户可以无缝集成任何喜欢的校验库。如果你并没有使用一个库,那也可以一直编写自己的逻辑来校验表单。

目前,官方支持了Yup, Joi and Superstruct的解析器。

npm install @hookform/resolvers

关于构建自定义解析器的说明:

  • 确保返回的对象包含valueserrors属性,并且它们的默认值应为{}

  • 返回error对象的键应和表单项的name值匹配。

  • 该函数将被缓存在的自定义hook中,而context则是每次重渲染都会变更的可变对象。

  • 用户交互时,输入组件只会有一个表单项发生重新校验。库本身会计算错误对象,并且触发相应的重渲染。

import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from "yup";

const schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required(),
});

const App = () => {
  const { register, handleSubmit } = useForm({
    resolver: yupResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(d => console.log(d))}>
      <input {...register("name")} />
      <input type="number" {...register("age")} />
      <input type="submit" />
    </form>
  );
};import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from "yup";

type Inputs = {
  name: string;
  age: string;
};

const schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required(),
});

const App = () => {
  const { register, handleSubmit } = useForm<Inputs>({
    resolver: yupResolver(schema), // yup, joi and even your own.
  });

  return (
    <form onSubmit={handleSubmit(d => console.log(d))}>
      <input {...register("name")} />
      <input type="number" {...register("age")} />
      <input type="submit" />
    </form>
  );
};
import React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";

const schema = z.object({
  name: z.string(),
  age: z.number()
});

const App = () => {
  const { register, handleSubmit } = useForm({
    resolver: zodResolver(schema)
  });

  return (
    <form onSubmit={handleSubmit(d => console.log(d)}>
      <input {...register("name")} />
      <input {...register("age")} type="number" />
      <input type="submit" />
    </form>
  );
};

export default App;
import React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";

const schema = z.object({
  name: z.string(),
  age: z.number()
});

type Schema = z.infer<typeof schema>;

const App = () => {
  const { register, handleSubmit } = useForm<Schema>({
    resolver: zodResolver(schema)
  });

  const onSubmit = (data: IFormInput) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name")} />
      <input {...register("age")} type="number" />
      <input type="submit" />
    </form>
  );
};

export default App;
import React from 'react';
import { useForm } from 'react-hook-form';
import { joiResolver } from '@hookform/resolvers/joi';
import Joi from "joi";

const schema = Joi.object({
  name: Joi.string().required(),
  age: Joi.string().required(),
});

const App = () => {
  const { register, handleSubmit } = useForm({
    resolver: joiResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(d => console.log(d)}>
      <input {...register("name")} />
      <input type="number" {...register("age")} />
      <input type="submit" />
    </form>
  );
};
import React from "react";
import { useForm } from "react-hook-form";
import { joiResolver } from "@hookform/resolvers/joi";
import Joi from "joi";

interface IFormInput {
  name: string;
  age: number;
}

const schema = Joi.object({
  name: Joi.string().required(),
  age: Joi.number().required()
});

const App = () => {
  const { register, handleSubmit, errors } = useForm<IFormInput>({
    resolver: joiResolver(schema)
  });
  const onSubmit = (data: IFormInput) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name"} />
      <input type="number" {...register("age"} />
      <input type="submit" />
    </form>
  );
}
import React from 'react';
import { useForm } from 'react-hook-form';
import { superstructResolver } from '@hookform/resolvers/superstruct';
import { struct } from 'superstruct';

const schema = struct({
  name: 'string',
  age: 'number',
});

const App = () => {
  const { register, handleSubmit } = useForm({
    resolver: superstructResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(d => console.log(d))}>
      <input {...register("name")} />
      <input {...register("age")} type="number" />
      <input type="submit" />
    </form>
  );
};
import * as React from 'react';
import { useForm } from 'react-hook-form';
import { vestResolver } from '@hookform/resolvers/vest';
import vest, { test, enforce } from 'vest';

const validationSuite = vest.create((data = {}) => {
  test('username', 'Username is required', () => {
    enforce(data.username).isNotEmpty();
  });

  test('username', 'Must be longer than 3 chars', () => {
    enforce(data.username).longerThan(3);
  });

  test('password', 'Password is required', () => {
    enforce(data.password).isNotEmpty();
  });

  test('password', 'Password must be at least 5 chars', () => {
    enforce(data.password).longerThanOrEquals(5);
  });

  test('password', 'Password must contain a digit', () => {
    enforce(data.password).matches(/[0-9]/);
  });

  test('password', 'Password must contain a symbol', () => {
    enforce(data.password).matches(/[^A-Za-z0-9]/);
  });
});

const App = () => {
  const { register, handleSubmit } = useForm({
    resolver: vestResolver(validationSuite),
  });

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <input {...register("username")} />
      <input {...register("password")} />
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";
import * as Joi from "joi";

const validationSchema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
});

const App = () => {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: async data => {
      const { error, value: values } = validationSchema.validate(data, {
        abortEarly: false
      });

      return {
        values: error ? {} : values,
        errors: error
          ? error.details.reduce((previous, currentError) => {
              return {
                ...previous,
                [currentError.path[0]]: currentError
              };
            }, {})
          : {}
      };
    }
  });

  const onSubmit = data => {
    console.log(data)
  };

  return (
    <div className="App">
      <h1>resolver</h1>
      
      <form onSubmit={handleSubmit(onSubmit)}>
        <label>Username</label>
        <input {...register("username")} />
        {errors.username && <p>errors.username.message</p>}
        <input type="submit" />
      </form>
    </div>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";
import * as Joi from "joi";

interface IFormInputs {
  username: string;
}

const validationSchema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
});

const App = () => {
  const { register, handleSubmit, errors } = useForm<IFormInputs>({
    resolver: async data => {
      const { error, value: values } = validationSchema.validate(data, {
        abortEarly: false
      });

      return {
        values: error ? {} : values,
        errors: error
          ? error.details.reduce((previous, currentError) => {
              return {
                ...previous,
                [currentError.path[0]]: currentError
              };
            }, {})
          : {}
      };
    }
  });

  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <div className="App">
      <h1>resolver</h1>

      <form onSubmit={handleSubmit(onSubmit)}>
        <label>Username</label>
        <input {...register("username")} />
        {errors.username && <p>errors.username.message</p>}
        <input type="submit" />
      </form>
    </div>
  );
};
shouldUnregister: boolean = true CodeSandbox
truefalse
Can you unregister an input?
Value remains when input unmounts?
Is form state updated?
eg: isValid, isDirty, touched
❌ you will need to clear manually
context:
object

该上下文对象将被注入resolver的第二个参数或校验的上下文对象。

CodeSandbox
criteriaMode
firstError | all

默认行为firstError将校验 所有字段的校验,并收集所有遇到的第一个错误。

设置为all时,所有字段的校验将是 运行并收集遇到的所有错误。

CodeSandbox
shouldFocusError:
boolean = true

默认情况下,当用户提交表单并包含错误时, 具有错误的第一个字段将被聚焦。

注意: 只有具有ref的注册字段将工作。 手动注册的输入将无法正常工作。 比如: register('test') // 不会工作

register: (Ref, RegisterOptions?) => voidReact Native: Custom register or using Controller

该方法允许您将输入组件/选择组件中的Ref和校验规则注册到 React Hook Form 中。

校验规则都是基于HTML标准,也允许自定义校验。

重要: name必需唯一的(非数字起始)。 输入组件名称还支持点和方括号语法,它允许您轻松创建嵌套的表单项。

Input 名字提交结果
name="firstName"{ firstName: 'value'}
name="name.firstName"{ name: { firstName: 'value' } }
name="name.firstName[0]"{ name: { firstName: [ 'value' ] } }

如果用在简单的表单项列表上,则可以将输入名称指定为name[index] 查看Field Array数组示例。 进阶用法可以查看 useFieldArray.

自定义注册

您还可以手动注册输入组件,这在使用自定义组件和无法访问Ref的情况下非常有用。在使用React Native或自定义组件(如react-select)时很常见。为了使用简单,我们提供了 组件来为您处理这个过程。

如果你决定不使用Controller来手动注册表单项, 那么就需要使用 更新输入组件的值。

register('firstName', { required: true, min: 8 })

注意: 如果你想要自定义组件在值更新时触发一次重渲染,那么你应该为注册的输入组件提供一个类型。

register({ name: 'firstName', type: 'custom' }, { required: true, min: 8 })

注意: 如果多个单选框类型的输入组件都有一样的name,则需要在最后一个输入组件注册校验规则以使 hook 知道要将它们一起校验。

注册选项

通过选择该选项,下面的API表将得到更新。

名字説明程式码范例
ref
React.RefObject
React element ref
<input name="test" ref={register} />
required
boolean
布尔项,true表示输入必须有值,才能提交表单。 您可以在errors中赋值为字符串以返回错误消息。
<input
  name="test"
  ref={
    register({
      required: true
    })
  }
/>
maxLength
number
文本框能接受的最大字符数。
<input
  name="test"
  ref={
    register({
      maxLength: 2
    })
  }
/>
minLength
number
文本框能输入的最小字符数。
<input
  name="test"
  ref={
    register({
      minLength: 1
    })
  }
/>
max
number
接受的最大值。
<input
  name="test"
  type="number"
  ref={
    register({
      max: 3
    })
  }
/>
min
number
接受的最小值。
<input
  name="test"
  type="number"
  ref={
    register({
      min: 3
    })
  }
/>
pattern
RegExp
校验文本框内容的正则表达式。
<input
  name="test"
  ref={
    register({
      pattern: /[A-Za-z]{3}/
    })
  }
/>
validate
Function | Object
您可以把回调函数作为参数传给validate,或者传入值为回调函数的对象来逐个校验。(参考例子)
<input
  name="test"
  ref={
    register({
      validate: value => value === '1'
    })
  }
/>
// object of callback functions
<input
  name="test1"
  ref={
    register({
      validate: {
        positive: value => parseInt(value) > 0,
        lessThanTen: value => parseInt(value) < 10,
        asyncValidate: async () => await fetch(url)
      }
    })
  }
/>
valueAsNumber:
boolean

Returns a Number normally. If something goes wrong NaN will be returned.

Note: valueAs process is happening after validation.

<input
  name="test"
  type="number"
  ref={
    register({
      valueAsNumber: true,
    })
  }
/>
valueAsDate:
boolean

Returns a Date normally. If something goes wrong null will be returned.

Note: valueAs process is happening after validation.

<input
  name="test"
  type="date"
  ref={
    register({
      valueAsDate: true,
    })
  }
/>
setValueAs:
<T>(value: any) => T

Return input value by running through the function.

Note: valueAs process is happening after validation. Also, setValueAs is ignored if either valueAsNumber or valueAsDate are true.

<input
  name="test"
  ref={
    register({
      setValueAs: (value) => parseInt(value),
    })
  }
/>

unregister: (name: string | string[]) => void

此方法允许您unregister单个或一组输入组件。

注意: 取消注册输入时,它的值将不再被包含在被提交的表单数据中。

import React, { useEffect } from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit, unregister } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button type="button" onClick={() => unregister("lastName")}>unregister</button>
      <input type="submit" />
    </form>
  );
}import React, { useEffect } from "react";
import { useForm } from "react-hook-form";

interface IFormInputs {
  firstName: string;
  lastName?: string;
}

export default function App() {
  const { register, handleSubmit, unregister } = useForm<IFormInputs>();
  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button type="button" onClick={() => unregister("lastName")}>unregister</button>
      <input type="submit" />
    </form>
  );
};


errors: Record<string, Object>

包含每个输入组件表单错误和错误消息的对象。

名字类型説明
typestringError 类型. eg: required, min, max, minLength
typesRecord<{ string, string | boolean }>在需要某个输入组件所有的校验错误时使用。比如;一个必填的密码框 同时限定了最小长度以及需要包含的特殊字符串 。
messagestring | React.ReactElement默认情况下消息是空字符串。 但是,如果注册时配置了错误信息,那么它将被返回。
refReact.Ref输入元素的引用。

注意: 避免使用对象键作为名称以致数据被覆盖。
eg: register('user'); register('user.type'); // error's type will get overwritten.

注意: 您可以使用 组件展示错误状态。

CodeSandbox
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, errors, handleSubmit } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("singleErrorInput", { required: true })} />
      {errors.singleErrorInput && "Your input is required"}

      {/* refer to the type of error to display message accordingly */}
      <input {...register("multipleErrorInput", { required: true, maxLength: 50 })} />
      {errors.multipleErrorInput?.type === "required" && "Your input is required"}
      {errors.multipleErrorInput?.type === "maxLength" && "Your input exceed maxLength"}

      {/* register with validation */}
      <input type="number" {...register("numberInput", { min: 50 })} />
      {errors.numberInput && "Your input required to be more than 50"}

      {/* register with validation and error message */}
      <input {...register("errorMessage", { required: "This is required" })} />
      {errors.errorMessage?.message}
      
      <input type="submit" />
    </form>
  );
}
import * as React from 'react'
import { useForm } from "react-hook-form";

interface IFormInputs {
  singleErrorInput: string
  multipleErrorInput: string
  numberInput: string
}

function App() {
  const { register, formState: { errors }, handleSubmit } = useForm<IFormInputs>();

  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Error</label>
      <input {...register("singleErrorInput", { required: true })} />
      {errors.singleErrorInput && <p>Your input is required</p>}

      <label>Error with type check</label>
      <input {...register("multipleErrorInput", { required: true, minLength: 5 })} />
      {errors.multipleErrorInput?.type === "required" && (
        <p>Your input is required</p>
      )}
      {errors.multipleErrorInput?.type === "minLength" && (
        <p>Your input must be larger then 3 characters</p>
      )}

      <label>Error with value</label>
      <input type="number" {...register("numberInput", { min: 50 })} />
      {errors.numberInput && <p>Your input required to be more than 50</p>}

      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";
type Inputs = {
  a: number;
  b: string;
  c: Date;
  d: {
    e: string;
  };
  f: {
    g: number[];
    h: string[];
    i: { j: string }[];
  };
  k: any;
  l: any[];
  m: unknown;
  n: unknown[];
  o: object;
  p: object[];
  q: {
    r: any;
    s: {
      t: any[];
      u: unknown;
      v: object;
    }[];
    w: Date[];
    x: {
      y: {
        z: object[];
      };
    };
  };
};
export default function App() {
  const { errors } = useForm<Inputs>();
  console.log(errors?.a?.message);
  console.log(errors?.b?.message);
  console.log(errors?.c?.message);
  console.log(errors?.d?.e?.message);
  console.log(errors?.f?.g && errors.f.g[0] && errors.f.g[0].message
  );
  console.log(errors?.f?.h && errors.f.h[0] && errors.f.h[0].message
  );
  console.log(
      errors?.f?.i &&
      errors?.f?.i[0] &&
      errors.f.i[0].j &&
      errors.f.i[0].j.message
  );
  console.log(errors?.k?.message);
  console.log(errors?.l?.message);
  console.log(errors?.m?.message);
  console.log(errors?.n && errors.n[0] && errors.n[0].message);
  console.log(errors?.o?.message);
  console.log(errors?.p && errors.p[0] && errors.p[0].message);
  console.log(errors?.q?.r?.message);
  console.log(
    errors?.q?.s && errors.q.s[0] && errors.q.s[0].t.message
  );
  console.log(
      errors?.q?.s &&
      errors.q.s[0] &&
      errors.q.s[0].u &&
      errors.q.s[0].u.message
  );
  console.log(
      errors?.q?.s &&
      errors.q.s[0] &&
      errors.q.s[0].v &&
      errors.q.s[0].v.message
  );
  console.log(errors?.q?.w && errors.q.w[0] && errors.q.w[0].message
  );
  console.log(
      errors?.q?.x?.y?.z &&
      errors.q.x.y.z[0] &&
      errors.q.x.y.z[0].message
  );
  return <form />;
}
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, formState: { errors }, handleSubmit } = useForm({
    // by setting criteriaMode to 'all', 
    // all validation errors for single field will display at once
    criteriaMode: "all"
  });
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="password"
        {...register("password", { required: true, minLength: 10, pattern: /\d+/ })}
      />
      {/* without enter data for the password input will result both messages to appear */}
      {errors?.password?.types?.required && <p>password required</p>}
      {errors?.password?.types?.minLength && <p>password minLength 10</p>}
      {errors?.password?.types?.pattern && <p>password number only</p>}

      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

interface IFormInputs {
  password: string
}

export default function App() {
  const { register, errors, handleSubmit } = useForm<IFormInputs>({
    // by setting criteriaMode to 'all',
    // all validation errors for single field will display at once
    criteriaMode: "all",
    mode: "onChange"
  });

  const onSubmit = (data: IFormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Password</label>
      <input
        type="password"
        {...register("password", { required: true, minLength: 10, pattern: /d+/gi })}
      />
      {/* without enter data for the password input will result both messages to appear */}
      {errors?.password?.types?.required && <p>password required</p>}
      {errors?.password?.types?.minLength && <p>password minLength 10</p>}
      {errors?.password?.types?.pattern && <p>password number only</p>}

      <input type="submit" />
    </form>
  );
}

watch: (names?: string | string[]) => anyVideo

监听制定的表单项并返回他们的值,可用于监听决定重渲染的表单项。

  • 未定义defaultValue时,watch首次渲染会返回undefined,因为它在register之前被调用,但是您可以将defaultValue设置为第二个参数。

  • 如果defaultValues在使用表单useForm作为参数初始化,则首次渲染将返回defaultValues中提供的内容。

  • 对于useFieldArray来说,当它包含的所有的表单项都被移除后,将会返回defaultValues,可以通过查看fields.length来确定这一点。

类型説明例子Return
string通过名称监听输入组件(类似于lodash的get函数)watch('inputName')
watch('inputName', 'value')
any
string[]监听多个输入组件watch(['inputName1'])
watch(['field1'], { field1: '1' })
{ [key:string] : any }
undefined监听所有输入组件watch()
watch(undefined, { field: '1' })
{ [key:string] : any }
CodeSandbox
import React from "react";
import { useForm } from "react-hook-form";

function App() {
  const { register, watch, formState: { errors }, handleSubmit } = useForm();
  const watchShowAge = watch("showAge", false); // you can supply default value as second argument
  const watchAllFields = watch(); // when pass nothing as argument, you are watching everything
  const watchFields = watch(["showAge", "number"]); // you can also target specific fields by their names

  // Callback version of watch.  It's your responsibility to unsubscribe when done.
  React.useEffect(() => {
    const subscription = watch((value, { name, type }) => console.log(value, name, type));
    return () => subscription.unsubscribe();
  }, [watch]);

  const onSubmit = data => console.log(data);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input type="checkbox" {...register("showAge")} />
        
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && <input type="number" {...register("age", { min: 50 })} />}
        
        <input type="submit" />
      </form>
    </>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

interface IFormInputs {
  name: string
  showAge: boolean
  age: number
}

function App() {
  const { register, watch, formState: { errors }, handleSubmit } = useForm<IFormInputs>();
  const watchShowAge = watch("showAge", false); // you can supply default value as second argument
  const watchAllFields = watch(); // when pass nothing as argument, you are watching everything
  const watchFields = watch(["showAge", "age"]); // you can also target specific fields by their names

  // Callback version of watch.  It's your responsibility to unsubscribe when done.
  React.useEffect(() => {
    const subscription = watch((value, { name, type }) => console.log(value, name, type));
    return () => subscription.unsubscribe();
  }, [watch]);

  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input {...register("name", { required: true, maxLength: 50 })} />
        <input type="checkbox" {...register("showAge")} />
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && (
          <input type="number" {...register("age", { min: 50 })} />
        )}
        <input type="submit" />
      </form>
    </>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

type Inputs = {
  key1: string;
  key2: number;
  key3: {
    key1: number;
    key2: boolean;
  };
};

export default function App(props) {
  const { watch } = useForm<FormValues>;

  watch();
  // function watch(): FormValues
  watch({ nest: true });
  // function watch(option: { nest: boolean; }): FormValues
  watch("key1");
  // function watch<"key1">(field: "key1", defaultValue?: string | undefined): string
  watch("key1", "test");
  // function watch<"key1">(field: "key1", defaultValue?: string | undefined): string
  watch("key1", true);
  // ❌: type error
  watch("key3.key1");
  // function watch<unknown>(field: string, defaultValue?: unknown): unknown
  watch("key3.key1", 1);
  // function watch<1>(field: string, defaultValue?: 1 | undefined): number
  watch("key3.key1", "test");
  // function watch<"key3.key1", "test">(field: "key3.key1", defaultValue?: string | undefined): string
  watch("key3.key2", true);
  // function watch<true>(field: string, defaultValue?: true | undefined): boolean
  watch(["key1", "key2"]);
  // function watch<"key1" | "key2">(fields: ("key1" | "key2")[], defaultValues?: DeepPartial<Pick<FormValues, "key1" | "key2">> | undefined): Pick<FormValues, "key1" | "key2">
  watch(["key1", "key2"], { key1: "test" });
  // function watch<"key1" | "key2">(fields: ("key1" | "key2")[], defaultValues?: DeepPartial<Pick<FormValues, "key1" | "key2">> | undefined): Pick<FormValues, "key1" | "key2">
  watch(["key1", "key2"], { key1: "test", key2: true });
  // ❌: type error
  watch(["key1", "key3.key1"], { key1: "string" });
  // function watch(fields: string[], defaultValues?: DeepPartial<FormValues> | undefined): DeepPartial<FormValues>
  watch(["key1", "key3.key1"], { test: "string" });
  // ❌: type error
  watch<string, FormData["key3"]["key1"]>("key3.key1");
  //  => string
  watch<string, FormData["key3"]["key2"]>("key3.key2");
  //  => string
  
  return <form />;
}
import * as React from "react";
import { useForm, useFieldArray, ArrayField } from "react-hook-form";

function App() {
  const { register, control, handleSubmit, watch } = useForm();
  const { fields, remove, append } = useFieldArray({
    name: "test",
    control
  });
  const onSubmit = (data: FormValues) => console.log(data);
  
  console.log(watch("test")); 

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {fields.map((field, index) => {
        return (
          <input
            key={field.id}
            defaultValue={field.firstName}
            {...register(`test[${index}].firstName`)}
          />
        );
      })}
      <button
        type="button"
        onClick={() =>
          append({
            firstName: "bill" + renderCount,
            lastName: "luo" + renderCount
          })
        }
      >
        Append
      </button>
    </form>
  );
}

handleSubmit:
((data: Object, e?: Event) => void, (errors: Object, e?: Event) => void) => Function

当表单校验成功时,此函数将传递表单数据并可以远程调用。

handleSubmit(onSubmit)()

注意: 您可以传递async函数用于异步校验。例如:

handleSubmit(async (data) => await fetchAPI(data))

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit = (data, e) => console.log(data, e);
  const onError = (errors, e) => console.log(errors, e);

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button type="submit">Submit</button>
    </form>
  );
}import React from "react";
import { useForm, SubmitHandler } from "react-hook-form";

type FormValues = {
  firstName: string;
  lastName: string;
  email: string;
};

export default function App() {
  const { register, handleSubmit } = useForm<FormValues>();
  const onSubmit: SubmitHandler<FormValues> = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <input name="email" type="email" ref={register} />

      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

function App() {
  const { register, handleSubmit, errors, formState } = useForm();
  const onSubmit = async data => {
    await sleep(2000);
    if (data.username === "bill") {
      alert(JSON.stringify(data));
    } else {
      alert("There is an error");
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="username">User Name</label>
      <input name="username" placeholder="Bill" ref={register} />

      <input type="submit" />
    </form>
  );
}

reset: (values?: Record<string, any>, omitResetState?: Record<string, boolean>) => void

此函数将重置表单中的字段值和错误。通过提供omitResetState,您就可以 部分状态。可以将values作为可选参数传递,以将表单重置为默认值。

注意: 对于像React-Select这样没有暴露ref的受控组件,您必须通过手动重置或使用来包装您的受控组件。

注意: 您将需要在useForm传入defaultValues来重置Controller组件的值。

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit, reset } = useForm();
  const onSubmit = (data, e) => {};
  
  useEffect(async () => {
    const result = await fetch('./api/formValues.json'); // result: { firstName: 'test', lastName: 'test2' }
    reset(result); // asynchronously reset your form values
  }, [reset])

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register} />
      <input type="reset" /> // standard reset button
      <input type="button" onClick={() => reset({ firstName: "bill" }); }} /> // reset form with values
      <input type="button" onClick={() => {
        reset({
          firstName: "bill"
        }, {
          errors: true, // errors will not be reset 
          dirtyFields: true, // dirtyFields will not be reset
          isDirty: true, // dirty will not be reset
          isSubmitted: false,
          touched: false,
          isValid: false,
          submitCount: false,
        });
      }} />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

interface UseFormInputs {
  firstName: string
  lastName: string
}

export default function Form() {
  const { register, handleSubmit, reset, errors } = useForm<UseFormInputs>();
  const onSubmit = (data: UseFormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>First name</label>
      <input name="firstName" ref={register({ required: true })} />

      <label>Last name</label>
      <input name="lastName" ref={register} />

      <input type="submit" />
      <input
        type="reset"
        value="Standard Reset Field Values"
      />
      <input
        type="button"
        onClick={() => reset()}
        value="Custom Reset Field Values & Errors"
      />
    </form>
  );
}
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";

export default function App() {
  const { register, handleSubmit, reset, setValue, control } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller 
        as={TextField} 
        name="firstName"
        control={control} 
        rules={ required: true } 
        defaultValue=""
      />
      <Controller 
        as={TextField} 
        name="lastName"
        control={control}
        defaultValue="" 
      />
      
      <input type="submit" />
      <input type="button" onClick={reset} />
      <input
        type="button"
        onClick={() => {
          reset({
            firstName: "bill",
            lastName: "luo"
          });
        }}
      />
    </form>
  );
}
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";

interface IFormInputs {
  firstName: string
  lastName: string
}

export default function App() {
  const { register, handleSubmit, reset, setValue, control } = useForm<IFormInputs>();
  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller 
        as={TextField} 
        name="firstName"
        control={control} 
        rules={ required: true } 
        defaultValue=""
      />
      <Controller 
        as={TextField} 
        name="lastName"
        control={control}
        defaultValue="" 
      />
      
      <input type="submit" />
      <input type="button" onClick={reset} />
      <input
        type="button"
        onClick={() => {
          reset({
            firstName: "bill",
            lastName: "luo"
          });
        }}
      />
    </form>
  );
}
import React from "react";
import { useForm, useFieldArray, Controller } from "./src";

function App() {
  const {
    register,
    handleSubmit,
    reset,
    formState: { isSubmitSuccessful }
  } = useForm({ defaultValues: { something: "anything" } });
  const [submittedData, setSubmittedData] = React.useState({});

  const onSubmit = (data) => {
    setSubmittedData(data);
  };

  React.useEffect(() => {
    if (isSubmitSuccessful) {
      reset({ ...submittedData });
    }
  }, [isSubmitSuccessful, submittedData, reset]);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="something" ref={register} />
      <input type="submit" />
    </form>
  );
}
import React, { useEffect } from "react";
import { useForm, useFieldArray, Controller } from "react-hook-form";

function App() {
  const { register, control, handleSubmit, reset } = useForm({
    defaultValues: {
      loadState: "unloaded",
      names: [{ firstName: "Bill", lastName: "Luo" }]
    }
  });
  const { fields, remove } = useFieldArray({
    control,
    name: "names"
  });

  // it's import to invoke reset after useFieldArray
  useEffect(() => {
    setTimeout(() => {
      reset({
        names: [
          {
            firstName: "Bill1",
            lastName: "Luo1"
          },
        ]
      });
    }, 1000);
  }, [reset]);

  const onSubmit = (data) => console.log("data", data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <ul>
        {fields.map((item, index) => (
          <li key={item.id}>
            <input
              name={`names[${index}].firstName`}
              defaultValue={`${item.firstName}`}
              ref={register()}
            />

            <Controller
              as={<input />}
              name={`names[${index}].lastName`}
              control={control}
              defaultValue={item.lastName}
            />
          </li>
        ))}
      </ul>

      <input type="submit" />
    </form>
  );
}

setError:
(name: string, error: { type?: string, types?: object, message?: string, shouldFocus?: boolean }) => void

该函数允许您手动设置输入单个或多项错误。

  • 如果输入组件的值通过校验,此方法将不会保留相关的输入错误。

  • 设置与输入组件字段无关的错误,并将在通过clearErrors手动删除之前一直保留。

  • 可用于handleSubmit函数调用后,向用户提供异步校验的错误反馈。(API返回了校验错误)

import React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm();
  const onSubmit = data => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="username"
        onChange={() => {
          setError("username", {
            type: "manual",
            message: "Dont Forget Your Username Should Be Cool!"
          });
        }}
        ref={register}
      />
      {errors.username && <p>{errors.username.message}</p>}
      <input type="submit" />
    </form>
  );
};import * as React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  username: string;
};

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm<FormInputs>();
  const onSubmit = (data: FormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="username"
        onChange={() => {
          setError("username", {
            type: "manual",
            message: "Dont Forget Your Username Should Be Cool!"
          });
        }}
        ref={register}
      />
      {errors.username && <p>{errors.username.message}</p>}
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm();

  const onSubmit = data => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Username</label>
      <input
        name="username"
        onChange={() => {
          setError("username", {
            type: "manual",
            message: "Dont Forget Your Username Should Be Cool!"
          });
        }}
        ref={register}
      />
      {errors.username && <p>{errors.username.message}</p>}
      <label>First Name</label>
      <input name="firstName" ref={register} />
      {errors.firstName && <p>{errors.firstName.message}</p>}
      <button
        type="button"
        onClick={() => {
          [
            {
              type: "manual",
              name: "username",
              message: "Double Check This"
            },
            {
              type: "manual",
              name: "firstName",
              message: "Triple Check This"
            }
          ].forEach(({ name, type, message }) =>
            setError(name, { type, message })
          );
        }}
      >
        Trigger Name Errors
      </button>
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  username: string;
  firstName: string;
};

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm<FormInputs>();

  const onSubmit = (data: FormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Username</label>
      <input
        name="username"
        onChange={() => {
          setError("username", {
            type: "manual",
            message: "Dont Forget Your Username Should Be Cool!"
          });
        }}
        ref={register}
      />
      {errors.username && <p>{errors.username.message}</p>}
      <label>First Name</label>
      <input name="firstName" ref={register} />
      {errors.firstName && <p>{errors.firstName.message}</p>}
      <button
        type="button"
        onClick={() => {
          [
            {
              type: "manual",
              name: "username",
              message: "Double Check This"
            },
            {
              type: "manual",
              name: "firstName",
              message: "Triple Check This"
            }
          ].forEach(({ name, type, message }) =>
            setError(name, { type, message })
          );
        }}
      >
        Trigger Name Errors
      </button>
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm({
    criteriaMode: 'all',
  });
  
  const onSubmit = data => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Last Name</label>
      <input
        name="lastName"
        ref={register}
        onChange={() => {
          setError("lastName", {
            types: {
              required: "This is required",
              minLength: "This is minLength"
            }
          });
        }}
      />
      {errors.lastName && errors.lastName.types && (
        <p>{errors.lastName.types.required}</p>
      )}
      {errors.lastName && errors.lastName.types && (
        <p>{errors.lastName.types.minLength}</p>
      )}
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  lastName: string;
};

const App = () => {
  const { register, handleSubmit, setError, errors } = useForm<FormInputs>({
    criteriaMode: 'all',
  });
  
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Last Name</label>
      <input
        name="lastName"
        ref={register}
        onChange={() => {
          setError("lastName", {
            types: {
              required: "This is required",
              minLength: "This is minLength"
            }
          });
        }}
      />
      {errors.lastName && errors.lastName.types && (
        <p>{errors.lastName.types.required}</p>
      )}
      {errors.lastName && errors.lastName.types && (
        <p>{errors.lastName.types.minLength}</p>
      )}
      <input type="submit" />
    </form>
  );
};

clearErrors: (name?: string | string[]) => void

  • undefined: 重置所有错误

  • string: 重置单个表单项错误

  • string[]: 重置指定所有表单项的错误

import * as React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, errors, handleSubmit, clearErrors } = useForm();

  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register({ required: true })} />
      <input name="username" ref={register({ required: true })} />
      <button type="button" onClick={() => clearErrors("firstName")}>
        Clear First Name Errors
      </button>
      <button
        type="button"
        onClick={() => clearErrors(["firstName", "lastName"])}
      >
        Clear First and Last Name Errors
      </button>
      <button type="button" onClick={() => clearErrors()}>
        Clear All Errors
      </button>
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

import "./styles.css";

type FormInputs = {
  firstName: string;
  lastName: string;
  username: string;
};

const App = () => {
  const { register, errors, handleSubmit, clearErrors } = useForm<FormInputs>();

  const onSubmit = (data: FormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register({ required: true })} />
      <input name="username" ref={register({ required: true })} />
      <button type="button" onClick={() => clearErrors("firstName")}>
        Clear First Name Errors
      </button>
      <button
        type="button"
        onClick={() => clearErrors(["firstName", "lastName"])}
      >
        Clear First and Last Name Errors
      </button>
      <button type="button" onClick={() => clearErrors()}>
        Clear All Errors
      </button>
      <input type="submit" />
    </form>
  );
};

setValue: (name: string, value: any, config?: Object) => void

该函数允许您动态设置注册过的表单项的值。 同时,它也会尝试避免不必要的重渲染。与只有以下情况将触发重渲染:

  • 值更新时未通过错误校验

  • 值更新时通过错误校验

  • 首次调用且formState.isDirty为true

可以将shouldValidate参数设置为true 以强制触发表单项的校验。

setValue('name', 'value', { shouldValidate: true })

您也可以将shouldDirty参数设置为true,以将表单项设置为dirty。

setValue('name', 'value', { shouldDirty: true })
CodeSandbox
import * as React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, handleSubmit, setValue } = useForm();

  const onSubmit = data => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button onClick={() => setValue("firstName", "Bill")}>
        Set First Name Value
      </button>
      <button
        onClick={() =>
          setValue("lastName", "Luo", {
            shouldValidate: true,
            shouldDirty: true
          })
        }
      >
        Set Last Name
      </button>
      <input type="submit" />
    </form>
  );
};
import * as React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  firstName: string
  lastName: string
}

const App = () => {
  const { register, handleSubmit, setValue } = useForm<FormInputs>();

  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button onClick={() => setValue("firstName", "Bill")}>
        Set First Name Value
      </button>
      <button
        onClick={() =>
          setValue("lastName", "Luo", {
            shouldValidate: true,
            shouldDirty: true
          })
        }
      >
        Set Last Name
      </button>
      <input type="submit" />
    </form>
  );
};
import React from "react";
import { useForm } from "react-hook-form";

type FormValues = {
  string: string;
  number: number;
  object: {
    number: number;
    boolean: boolean;
  };
  array: {
    string: string;
    boolean: boolean;
  }[];
};

export default function App() {
  const { setValue } = useForm<FormValues>();
  
  setValue("string", "test");
  // function setValue<"string", string>(name: "string", value: string, shouldValidate?: boolean | undefined): void
  setValue("number", 1);
  // function setValue<"number", number>(name: "number", value: number, shouldValidate?: boolean | undefined): void
  setValue("number", "error");
  
  return <form />;
}
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";

const ChildComponent = ({ index, control, register }) => {
  const { fields } = useFieldArray({
    name: `nest[${index}].nestedArray`,
    control
  });

  return (
    {fields.map((item, i) => (
      <input
        key={item.id}
        name={`nest[${index}].nestedArray[${i}].value`}
        ref={control.register()}
        defaultValue={item.value}
      />
    ))}
  );
};

export default () => {
  const { register, control, setValue } = useForm({
    defaultValues: {
      nest: [
        { value: "1", nestedArray: [{ value: "2" }] },
        { value: "3", nestedArray: [{ value: "4" }] }
      ]
    }
  });
  const { fields } = useFieldArray({ name: "nest", control });

  React.useEffect(() => {
    setValue("nest", [
      {
        value: 0,
        nestedArray: [
          {
            value: 8
          }
        ]
      }
    ]);
  }, [setValue]);

  return (
    <>
      {fields.map((item, i) => (
        <div key={item.id}>
          <input
            name={`nest[${i}].value`}
            ref={register()}
            defaultValue={item.value}
          />
          <ChildComponent control={control} register={register} index={i} />
        </div>
      ))}
    </>
  );
};

getValues: (payload?: string | string[]) => Object

优化过的读取表单值的方法。不会像watch一样触发重渲染及监听输入组件变化。

  • getValues():读取整个表单值。

  • getValues('test'):通过name读取单个输入组件值。

  • getValues(['test','test1']):通过name读取多个输入组件值。

CodeSandbox
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, getValues } = useForm();

  return (
    <form>
      <input name="test" ref={register} />
      <input name="test1" ref={register} />

      <button
        type="button"
        onClick={() => {
          const values = getValues(); // { test: "test-input", test1: "test1-input" }
          const singleValue = getValues("test"); // "test-input"
          const multipleValues = getValues(["test", "test1"]);
          // { test: "test-input", test1: "test1-input" }
        }}
      >
        Get Values
      </button>
    </form>
  );
}import React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  test: string
  test1: string
}

export default function App() {
  const { register, getValues } = useForm<FormInputs>();

  return (
    <form>
      <input name="test" ref={register} />
      <input name="test1" ref={register} />

      <button
        type="button"
        onClick={() => {
          const values = getValues(); // { test: "test-input", test1: "test1-input" }
          const singleValue = getValues("test"); // "test-input"
          const multipleValues = getValues(["test", "test1"]);
          // { test: "test-input", test1: "test1-input" }
        }}
      >
        Get Values
      </button>
    </form>
  );
}import React from "react";
import { useForm } from "react-hook-form";

// Flat input values
type Inputs = {
  key1: string;
  key2: number;
  key3: boolean;
  key4: Date;
};

export default function App() {
  const { register, getValues } = useForm<Inputs>();
  
  getValues();
  
  return <form />;
}

// Nested input values
type Inputs1 = {
  key1: string;
  key2: number;
  key3: {
    key1: number;
    key2: boolean;
  };
  key4: string[];
};

export default function Form() {
  const { register, getValues } = useForm<Inputs1>();
  
  getValues();
  // function getValues(): Record<string, unknown>
  getValues("key1");
  // function getValues<"key1", unknown>(payload: "key1"): string
  getValues("key2");
  // function getValues<"key2", unknown>(payload: "key2"): number
  getValues("key3.key1");
  // function getValues<"key3.key1", unknown>(payload: "key3.key1"): unknown
  getValues<string, number>("key3.key1");
  // function getValues<string, number>(payload: string): number
  getValues<string, boolean>("key3.key2");
  // function getValues<string, boolean>(payload: string): boolean
  getValues("key4");
  // function getValues<"key4", unknown>(payload: "key4"): string[]

  return <form />;
}

trigger: (payload?: string | string[]) => Promise<boolean>

手动触发表单校验。

  • trigger(): 触发所有表单项的校验。

  • trigger('test'): 触发name对应的单个表单项校验。

  • trigger(['test', 'test1']): 触发name对应的多个表单项校验。

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, trigger, errors } = useForm();

  return (
    <form>
      <input name="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register({ required: true })} />
      <button type="button" onClick={() => { trigger("lastName"); }}>Trigger</button>
      <button type="button" onClick={() => { trigger(["firstName", "lastName"]); }}>Trigger Multiple</button>
      <button type="button" onClick={() => { trigger(); }}>Trigger All</button>
      <button
        type="button"
        onClick={async () => {
          const result = await trigger("lastName");
          if (result) { console.log("valid input") }
        }}
      >
        Trigger Result
      </button>
    </form>
  );
}import React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  firstName: string
  lastName: string
}

export default function App() {
  const { register, trigger, errors } = useForm<FormInputs>();

  return (
    <form>
      <input name="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register({ required: true })} />
      <button type="button" onClick={() => { trigger("lastName"); }}>Trigger</button>
      <button type="button" onClick={() => { trigger(["firstName", "lastName"]); }}>Trigger Multiple</button>
      <button type="button" onClick={() => { trigger(); }}>Trigger All</button>
      <button
        type="button"
        onClick={async () => {
          const result = await trigger("lastName");
          if (result) { console.log("valid input") }
        }}
      >
        Trigger Result
      </button>
    </form>
  );
}

control: Object

为React Hook Form的Controller组件提供的对象,包含了 React Hook Form 注册受控组件所需的方法。

import React from "react";
import { useForm, Controller } from "react-hook-form";

function App() {
  const { control } = useForm();
  return (
    <Controller
      as={<input />}
      name="firstName"
      control={control}
      defaultValue=""
    />
  );
}
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";

type FormInputs = {
  firstName: string
}

function App() {
  const { control, handleSubmit } = useForm<FormInputs>();
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        as={TextField}
        name="firstName"
        control={control}
        defaultValue=""
      />
      
      <input type="submit" />
    </form>
  );
}

formState: Object

包含表单状态信息的对象。

重要:formState使用Proxy包装以提高渲染性能,因此请使您在渲染前先调用/读取它,以使状态可以更新。 减少重渲染的功能仅适用于Web平台,React Native因不支持Proxy而无法使用。

名字类型説明
isDirtyboolean在用户与任何输入组件交互后设置为true

注意:由于取消文件选择功能以及 FileList 对象,文件型输入组件需要在应用层管理。

dirtyFieldsobject用户更改过的表单项。
touchedobject包含用户交互过的所有表单项的对象。
isSubmittedboolean在用户提交表单后设置true。提交表单后,其状态将保持提交状态,直到使用reset方法调用为止。
isSubmitSuccessfulboolean

表示表格已成功提交。

isSubmittingboolean在表单提交将设置为true,提交后设置为false
submitCountnumber提交表单次数。
isValidboolean
如果没有任何错误,设置为true

Note: isValid is affected by mode. This state is only applicable with onChange and onBlur mode.

isValidatingboolean在验证期间设置为true
errorsobjectAn object with field errors.
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const {
    register,
    handleSubmit,
    errors,
    // Read the formState before render to subscribe the form state through Proxy
    formState: { isDirty, isSubmitting, touched, submitCount },
  } = useForm();
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="test" ref={register} />
      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";

type FormInputs = {
  test: string
}

export default function App() {
  const {
    register,
    handleSubmit,
    errors,
    // Read the formState before render to subscribe the form state through the Proxy
    formState: { isDirty, isSubmitting, touched, submitCount },
  } = useForm<FormInputs>();
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="test" ref={register} />
      <input type="submit" />
    </form>
  );
}

Controller: Component

React Hook Form优先采用非受控组件和原生的输入组件, 然而难以避免使用到第三方的受控组件,如React-Select, AntD Material-UI。 这个包装组件将使您的工作更轻松。

名字类型需要説明
namestring输入组件的唯一名称。
asReact.ElementType

ControlleronChangeonBlurvalue属性注入组件。

你传给Controller组件的所有属性都会传给as指向的组件。比如:你有一个自定义的Switch 组件,它需要label 属性, 你可以直接传给Controller,它会帮你传递给包装的组件。

注意: 以下属性将会传入你的组件onChange, onBlur, value.

<Controller 
  as={<TextInput />} 
  control={control} 
  name="test" 
/>
<Controller 
  as={TextInput} 
  control={control} 
  name="test" 
/>
renderFunction这是一个render prop。返回React元素并将事件和值附加到组件中的函数: 。这很容易与带有非标准属性名称的外部受控组件集成,为子组件提供onChangeonBlurvalue属性。 。
<Controller
                  control={control}
                  name='test'
                  render={({ onChange, onBlur, value }) => (
                    <Input
                      onTextChange={onChange}
                      onTextBlur={onBlur}
                      textValue={value}
                    />
                  )}
                />
                <Controller render={props => <Input {...props} />} />
controlObject通过调用useForm得到的对象。使用FormContext时可选。
defaultValueany与非受控组件的defaultValue相同,当传入boolean值时,它将被视为复选框输入。

注意: 需要在表单项层级使用defaultValue或在调用useForm时传入defaultValues

注意: 如果您的表单需要在reset时使用默认值,您将需要 在调用useForm时设置defaultValues而不是在单独的表单项上设置defaultValue

rulesObjectregister格式一致的校验规则
rules={{ required: true }}
onFocus() => void

此回调允许自定义hook在有错误情况聚焦到对应的输入组件。无论React和React-Native 只要组件可以聚焦,它都可以使用。

CodeSandbox
import React from "react";
import ReactDatePicker from "react-datepicker";
import { TextField } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";

function App() {
  const { handleSubmit, control } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller as={TextField} name="TextField" control={control} defaultValue="" />
      
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ onChange, onBlur, value }) => (
          <ReactDatePicker
            onChange={onChange}
            onBlur={onBlur}
            selected={value}
          />
        )}
      />
      
      <input type="submit" />
    </form>
  );
}
import React from "react";
import ReactDatePicker from "react-datepicker";
import { TextField } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";

type FormValues = {
  TextField: string;
  ReactDatepicker: string;
} 

function App() {
  const { handleSubmit, control } = useForm<FormValues>();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller as={TextField} name="TextField" control={control} defaultValue="" />
      
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ onChange, onBlur, value }) => (
          <ReactDatePicker
            onChange={onChange}
            onBlur={onBlur}
            selected={value}
          />
        )}
      />
      
      <input type="submit" />
    </form>
  );
}
Expo
import React from "react";
import { Text, View, TextInput, Button, Alert } from "react-native";
import { useForm, Controller } from "react-hook-form";

export default function App() {
  const { control, handleSubmit, formState: { errors } } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <View>
      <Controller
        control={control}
        rules={{
         required: true,
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={styles.input}
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
        name="firstName"
        defaultValue=""
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Controller
        control={control}
        rules={{
         maxLength: 100,
        }}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={styles.input}
            onBlur={onBlur}
            onChangeText={onChange}
            value={value}
          />
        )}
        name="lastName"
        defaultValue=""
      />

      <Button title="Submit" onPress={handleSubmit(onSubmit)} />
    </View>
  );
}

useController: (arguments: UseControllerProps) => { field: object, meta: object }

这个自定义的钩子是Controller的动力,并且分享了 和Controller一样的道具和方法。这对于 创建可重用的Controller输入,而Controller是 灵活的选项,可以放到您的页面或表单中。

请参考Controller部分,了解这个钩子的功能。 参数。除了as和 之外,它与其他参数相同。渲染

React.RefObject
Object Name名字类型説明
fieldonChange(value: any) => void

A function which send value to hook form and should be assigned with onChange prop.

onBlur(value: any) => void

A function which send value to hook form and should be assigned with onBlur prop.

valueunknown

The value of this controlled component.

ref

Assign ref to component's input ref, so hook form can focus on the error input.

metainvalidboolean

Invalid state for current input.

isTouchedboolean

Touched state for current controlled input.

isDirtyboolean

Dirty state for current controlled input.

import React from "react";
import { TextField } from "@material-ui/core";
import { useController, control } from "react-hook-form";

function Input({ control, name }) {
  const {
    field: { ref, ...inputProps },
    meta: { invalid, isTouched, isDirty },
  } = useController({
    name,
    control,
    rules: { required: true },
    defaultValue: "",
  });

  return <TextField {...inputProps} inputRef={ref} />;
}

function App() {
  const { control } = useForm();
  
  return <Input name="firstName" control={control} />;
}
import * as React from "react";
import { useForm, useController } from "./src";

function Input(props) {
  const { field, meta } = useController(props);

  return (
    <div>
      <input {...field} placeholder={props.name} />
      <p>{meta.isTouched && "Touched"}</p>
      <p>{meta.isDirty && "Dirty"}</p>
      <p>{meta.invalid ? "invalid" : "valid"}</p>
    </div>
  );
}

export default function App() {
  const { handleSubmit, control } = useForm({
    defaultValues: {
      FirstName: ""
    },
    mode: "onChange"
  });
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input control={control} name="FirstName" rules={{ required: true }} />
      <input type="submit" />
    </form>
  );
}

ErrorMessage: Component

展示输入组件错误信息的简易组件

npm install @hookform/error-message
名字类型需要説明
namestring表单项名称
errorsobjectReact Hook Form中的errors对象。如果您使用的是FormProvider,则为可选。
messagestring | React.ReactElement内联错误信息。
asReact.ElementType | string包装组件或HTML标签。 例如: as="span" or as={<Text />}
render({ message: string | React.ReactElement, messages?: Object}) => any这是一个 render prop 用于呈现单个或多个错误信息。

注意:您需要设置将criteriaMode设置为“all”以使用message.

import React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';

export default function App() {
  const { register, formState: { errors }, handleSubmit } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("singleErrorInput", { required: "This is required." })} />
      <ErrorMessage errors={errors} name="singleErrorInput" />
      
      <ErrorMessage
        errors={errors}
        name="singleErrorInput"
        render={({ message }) => <p>{message}</p>}
      />
      
      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';

interface FormInputs {
  singleErrorInput: string
}

export default function App() {
  const { register, formState: { errors }, handleSubmit } = useForm<FormInputs>();
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("singleErrorInput", { required: "This is required." })} />
      <ErrorMessage errors={errors} name="singleErrorInput" />
      
      <ErrorMessage
        errors={errors}
        name="singleErrorInput"
        render={({ message }) => <p>{message}</p>}
      />
      
      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';

export default function App() {
  const { register, errors, handleSubmit } = useForm({
    criteriaMode "all"
  });
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("multipleErrorInput", {
          required: "This is required.",
          pattern: {
            value: /d+/,
            message: "This input is number only."
          },
          maxLength: {
            value: 10,
            message: "This input exceed maxLength."
          }
        })}
      />
      <ErrorMessage errors={errors} name="multipleErrorInput">
        {({ messages }) =>
          messages &&
          Object.entries(messages).map(([type, message]) => (
            <p key={type}>{message}</p>
          ))
        }
      </ErrorMessage>

      <input type="submit" />
    </form>
  );
}import React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';

interface FormInputs {
  multipleErrorInput: string
}

export default function App() {
  const { register, errors, handleSubmit } = useForm<FormInputs>({
    criteriaMode "all"
  });
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("multipleErrorInput", {
          required: "This is required.",
          pattern: {
            value: /d+/,
            message: "This input is number only."
          },
          maxLength: {
            value: 10,
            message: "This input exceed maxLength."
          }
        })}
      />
      <ErrorMessage errors={errors} name="multipleErrorInput">
        {({ messages }) =>
          messages &&
          Object.entries(messages).map(([type, message]) => (
            <p key={type}>{message}</p>
          ))
        }
      </ErrorMessage>

      <input type="submit" />
    </form>
  );
}

useFormContext: Function

可以获取表单context的 hook函数 useFormContext用于那些难以传递context作为属性的嵌套结构很深的场景。

使用useFormContext需要用FormProvider包装表单。

Props

名字类型説明
...propsObjectAccept all useForm methods.
import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";

export default function App() {
  const methods = useForm();
  const onSubmit = data => console.log(data);

  return (
    <FormProvider {...methods} > // pass all methods into the context
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <NestedInput />
        <input type="submit" />
      </form>
    </FormProvider>
  );
}

function NestedInput() {
  const { register } = useFormContext(); // retrieve all hook methods
  return <input {...register("test")} />;
}

useWatch: ({ control?: Control, name?: string, defaultValue?: any }) => object

watch API功能相近,但是, 会在您的组件级别区隔重渲染,并可能为您的应用程序提供更好的性能。

Props

名字类型説明
namestring | string[]表单项名称
controlObject通过调用useForm得到的对象。使用FormContext时可选。
defaultValueany

default value for useWatch to return before the initial render.

Note: the first render will always return defaultValue when it's supplied.

disableboolean = false

Option to disable the subscription.

Rules

  • The initial return value from useWatch will always return what's inside of defaultValue or defaultValues from useForm.

  • The only different between useWatch and watch is at root level or component level update.

  • useWatch execution order matters, which means if you are update form value before the subscription is in place, then the value update will be ignored.

    setValue('test', 'data');
    useWatch('test'); // ❌ subscription is happened after value update, no update received
    
    useWatch('example'); // ✅ input value update will be received and trigger re-render
    setValue('example', 'data'); 
    
  • useWatch result is optimised for render phase instead of useEffect's deps, to detect value update you may want to use an external custom hook for value comparison.

import React from "react";
import { useForm, useWatch } from "react-hook-form";

function IsolateReRender({ control }) {
  const firstName = useWatch({
    control,
    name: 'firstName', // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both
    defaultValue: 'default' // default value before the render
  });

  return <div>{firstName}</div>; // only re-render at the component level, when firstName changes
}

function App() {
  const { register, control, handleSubmit } = useForm();
  
  return (
    <form onSubmit={handleSubmit(data => console.log("data", data))}>
      <input {...register("firstName")} />
      <input {...register("last")} />
      <IsolateReRender control={control} />
      
      <input type="submit" />
    </form>
  );
}
import React from "react";
import { useForm, useWatch } from "react-hook-form";

interface FormInputs {
  firstName: string
}

function FirstNameWatched({ control }: { control: Control<FormInputs> }) {
  const firstName = useWatch({
    control,
    name: "firstName", // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both
    defaultValue: "default" // default value before the render
  });

  return <p>Watch: {firstName}</p>; // only re-render at the component level, when firstName changes
}

function App() {
  const { register, control, handleSubmit } = useForm<FormInputs>();

  const onSubmit = (data: FormInputs) => {
    console.log(data)
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>First Name:</label>
      <input {...register("firstName")} />
      <input type="submit" />
      <FirstNameWatched control={control} />
    </form>
  );
}
import React from "react";
import { useWatch } from "react-hook-form";

function totalCal(results) {
  let totalValue = 0;

  for (const key in results) {
    for (const value in results[key]) {
      if (typeof results[key][value] === "string") {
        const output = parseInt(results[key][value], 10);
        totalValue = totalValue + (Number.isNaN(output) ? 0 : output);
      } else {
        totalValue = totalValue + totalCal(results[key][value], totalValue);
      }
    }
  }

  return totalValue;
}

export const Calc = ({ control, setValue }) => {
  const results = useWatch({ control, name: "test" });
  const output = totalCal(results);

  // isolated re-render to calc the result with Field Array
  console.log(results);

  setValue("total", output);

  return <p>{output}</p>;
};


useFieldArray:
({ control?: Control, name: string, keyName?: string = 'id' }) => objectVideo

处理非受控表单项列表(一组动态的输入组件)的自定义 hook。 用于提高用户体验和性能。可以观看 这个短视频 以比较受控/非受控的表单项列表。

名字类型需要説明
namestring表单项名称。重要: 确保名称为对象键结构: name=test[index].name 因为我们不支持扁平数组。
controlObject通过调用useForm得到的对象。使用FormContext时可选。
keyNamestring = 'id'表单项列表key的值,默认为“ id”,您可以 更改key的名称。
function Test() {
  const { control, register } = useForm();
  const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
    control, // control props comes from useForm (optional: if you are using FormContext)
    name: "test", // unique name for your Field Array
    // keyName: "id", default to "id", you can change the key name
  });

  return (
    {fields.map((field, index) => (
      <input
        key={field.id} // important to include key with field's id
        name={`test[${index}].value`}
        ref={register()} // register() when there is no validation rules 
        defaultValue={field.value} // make sure to include defaultValue
      />
    ))}
  );
}

重要: useFieldArray建立在非受控组件之上。 以下说明将帮助您了解并牢记其实施过程中的行为。

  • 可以通过在userform中的defaultValues来填充表单项列表

  • 确保您将来自fields对象的id分配为组件键。

  • 确保通过fields[index]设置defaultValue

  • 您不能在动作里调用其他的动作,而应该在每次渲染中触发动作。

    // ❌ The following is not correct
    handleChange={() => {
      if (fields.length === 2) {
        remove(0);
      }
      append({ test: 'test' });
    }}
    
    // ✅ The following is correct and second action is triggered after next render
    handleChange={() => {
      append({ test: 'test' });
    }}
    
    React.useEffect(() => {
      if (fields.length === 2) {
        remove(0);
      }
    }, [fields])
                
  • useFieldArray一起使用时,请使用ref={register()}而不是ref={register},这一点很重要,使得register将在映射期间被调用。

  • 它不适用于useEffect上的自定义注册或者是条件渲染。对于条件渲染的场景,尝试使用样式控制组件是否可见,以及控制校验函数。
  • watch整个表单项列表时,需要在fields 设置初始值以避免返回空值。例如: watch('fieldArray', fields)

  • 当所有的输入组件都从表单项列表移除时,watch 将返回 defaultValues。你可以使用fields.length来避免这个行为。例如;fields.length ? watch('fieldArray', fields) : []

名字类型説明
fieldsobject & { id: string }此对象是映射和渲染输入组件的数据来源

重要: 由于每个输入可以是非受控的, 因此id对React确认组件是已更改、添加或删除来说是必须的。

eg: {fields.map(d => <input key={d.id} />)}

append(obj: object | object[], shouldFocus?: boolean = true) => void将单个/多个输入组件追加到表单项列表的末尾并聚焦。
prepend(obj: object | object[], shouldFocus?: boolean = true) => void将单个/多个输入组件插入到表单项列表的头部并聚焦。
insert(index: number, value: object, shouldFocus?: boolean = true) => void将单个/多个输入组件插入到表单项列表的特定位置并聚焦。
swap(from: number, to: number) => void交换单个/多个输入组件的位置
move(from: number, to: number) => void将单个/多个输入组件移动到另一位置。
remove(index?: number | number[]) => void在特定位置删除单个/多个输入组件,不传入索引时删除列表内全部表单项。
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";

function App() {
  const { register, control, handleSubmit, reset, trigger, setError } = useForm({
    // defaultValues: {}; you can populate the fields by this attribute 
  });
  const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
    control,
    name: "test"
  });
  
  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <ul>
        {fields.map((item, index) => (
          <li key={item.id}>
            <input
              name={`test[${index}].firstName`}
              ref={register()}
              defaultValue={item.firstName} // make sure to set up defaultValue
            />
            <Controller
              as={<input />}
              name={`test[${index}].lastName`}
              control={control}
              defaultValue={item.lastName} // make sure to set up defaultValue
            />
            <button type="button" onClick={() => remove(index)}>Delete</button>
          </li>
        ))}
      </ul>
      <button
        type="button"
        onClick={() => append({ firstName: "appendBill", lastName: "appendLuo" })}
      >
        append
      </button>
      <input type="submit" />
    </form>
  );
}
import React from 'react';
import { useForm, useWatch, useFieldArray, Control } from 'react-hook-form';

const ConditionField = ({
  control,
  index,
}: {
  control: Control;
  index: number;
}) => {
  const output = useWatch<any>({
    name: 'data',
    control,
    defaultValue: 'yay! I am watching you :)',
  });

  return (
    <>
      {/* Required shouldUnregister: false */}
      {output[index]?.name === "bill" && (
        <input ref={control.register()} name={`data[${index}].conditional`} />
      )}
      {/* doesn't required shouldUnregister: false */}
      <input
        name={`data[${index}].easyConditional`}
        style={{ display: output[index]?.name === "bill" ? "block" : "none" }}
      />
    </>
  );
};

const UseFieldArrayUnregister: React.FC = () => {
  const { control, handleSubmit, register } = useForm<{
    data: { name: string }[];
  }>({
    defaultValues: {
      data: [{ name: 'test' }, { name: 'test1' }, { name: 'test2' }],
    },
    mode: 'onSubmit',
    shouldUnregister: false,
  });
  const { fields } = useFieldArray<{ name: string }>({
    control,
    name: 'data',
  });
  const onSubmit = (data: any) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {fields.map((data, index) => (
        <>
          <input
            name={`data[${index}].name`}
            defaultValue={data.name}
            ref={register()}
          />
          <ConditionField control={control} index={index} />
        </>
      ))}
      <input type="submit" />
    </form>
  );
};

进阶

了解如何使用React Hook Form构建复杂且易于访问的表单。

Edit