Skip to content

API.V6

は API をシンプルにすることにより、最高な DX を提供することに焦点を当てています。

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
NameType説明
onSubmit (Default)stringsubmit イベントからバリデーションがトリガーされ、 無効な入力は onChange イベントリスナーをアタッチして再度バリデーションを行います。
onBlurstringblur イベントからバリデーションがトリガーされます。
onChangestring入力の度に change イベントからバリデーションがトリガーされ、複数の再レンダリングが行われます。 非推奨: これをパフォーマンスの悪い習慣と考えてください。
onTouchedstring入力がタッチされるまでバリデーションが発生します。
allstring検証はblurおよびchangeイベントでトリガーされます。 警告:onChangeモードと同様に、allはパフォーマンスに大きな影響を与える可能性があります
reValidateMode: onChange | onBlur | onSubmit = 'onChange'React Native: Custom register or using Controller

このオプションを使用すると、エラーのある入力が再度バリデーションされるタイミングを設定することができます。 (デフォルトでは、入力変更時にバリデーションがトリガーされます。)

defaultValues: Record<string, any> = {}Video

defaultValue/defaultChecked を使用して input のデフォルト値を設定するか (詳細については React ドキュメントを参照) defaultValues を省略可能な引数として渡してフォーム全体のデフォルト値を設定することができます。

重要: defaultValues はカスタムフック内にキャッシュされます。 defaultValues をリセットしたい場合は、 API を使用してください。

注意: defaultValues で定義された値は defaultValue として に注入されます。

注意: 手動で register を適用したフィールドでは React Hook Form に ref を提供しないため、 手動で登録した input (例: register('test')) に defaultValues は自動設定されません。

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 を外部の(スキーマ)バリデーションライブラリとして制限するだけがゴールではありません。 私たちは React Hook Form で動作する多くのバリデーションライブラリをサポートしたいと思っています。 カスタムバリデーションロジックを作成して検証することもできます。

Yup、Joi、Superstructを正式にサポートしています 標準リゾルバー

npm install @hookform/resolvers

カスタムリゾルバの構築に関する注意:

  • 注意: values errors を含むオブジェクトを返していることを確認してください。 デフォルト値は空のオブジェクト {} である必要があります。

  • 注意: 返す errors オブジェクトのキーは、フォーム内の input(name属性)に関連させる必要があります。

  • 注意:この関数は カスタムフック内にキャッシュされますが、contextは 再レンダリングのたびに変更できる可変オブジェクト。

  • 注意: ライブラリ自体が特定のフィールドに対してエラーオブジェクトを評価し、 それに応じて再レンダリングをトリガーするため、ユーザーの入力中、一度に一つのフィールドでのみ input の再検証が発生します。

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(),
}).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(),
}).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>
  );
};
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: Schema) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name")} />
      <input {...register("age")} type="number" />
      <input type="submit" />
    </form>
  );
};
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, formState: { 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 { useForm } from 'react-hook-form';
import { ajvResolver } from '@hookform/resolvers/ajv';

// must use `minLength: 1` to implement required field
const schema = {
  type: 'object',
  properties: {
    username: {
      type: 'string',
      minLength: 1,
      errorMessage: { minLength: 'username field is required' },
    },
    password: {
      type: 'string',
      minLength: 1,
      errorMessage: { minLength: 'password field is required' },
    },
  },
  required: ['username', 'password'],
  additionalProperties: false,
};

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

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <input {...register('username')} />
      {errors.username && <p>{errors.username.message}</p>}
      <input {...register('password')} />
      {errors.password && <p>{errors.password.message}</p>}
      <button type="submit">submit</button>
    </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({
    context: "context",
    resolver: async (data, context) => {
      const { error, value: values } = validationSchema.validate(data, {
        abortEarly: false,
      });

      if (!error) return { values: values, errors: {} };

      return {
        values: {},
        errors: error.details.reduce(
          (previous, currentError) => ({
            ...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, formState: { 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

デフォルトでは、入力が削除されると、React Hook FormはMutationObserverを使用して、マウント解除された入力を検出および登録解除します。ただし、shouldUnregisterfalseに設定して、マウント解除による入力状態の損失を防ぐことができます。

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

このコンテキストオブジェクトは、validationResolver の第二引数または Yup バリデーションのコンテキストオブジェクトに渡されます。

CodeSandbox
criteriaMode
firstError | all

デフォルトの設定である firstError は、全てのフィールドのバリデーションを行い、最初に発生したエラーを収集します。

このオプションを all に設定すると、全てのフィールドのバリデーションが実行され、 発生した全てのエラーが収集されます。

CodeSandbox
shouldFocusError:
boolean = true

デフォルトでは、ユーザーがフォームを送信してそのフォームにエラーが含まれている時、 エラーのある最初のフィールドがフォーカスされます。

注意: ref を持つ登録したフィールドのみが機能します。 手動で登録した input では機能しません。例: register('test') // doesn't work

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

このメソッドを使用すると、input/select の Ref とバリデーションルールを React Hook Form に登録 (register) することができます。

バリデーションルールは全て HTML 標準に基づいており、カスタムバリデーションも可能です。

重要: name 属性は必須かつユニークです。 input の name 属性はドット記法およびブラケット記法もサポートしているため、 ネストされたフォームフィールドを簡単に作成することができます。例は下記の通りです:

Input Nameフォームの送信結果
name="firstName"{ firstName: 'value'}
name="name.firstName"{ name: { firstName: 'value' } }
name="name.firstName[0]"{ name: { firstName: [ 'value' ] } }

arrays/array フィールドを使用する場合、input の name 属性を name[index] のように割り当てることができます。フィールド配列の例をご覧ください

カスタム登録

また、input を手動で登録 (manual register) することもできます。 これは、カスタムコンポーネントを操作していて Ref にアクセスできない場合に便利です。 React Native や react-select のようなカスタムコンポーネントを使用している場合がこれに該当します。

カスタム登録を使用すると、 input の ref は登録されていないため、 で入力値を更新する必要があります。

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

注意: 値の更新中に、カスタム登録した input で再レンダリングをトリガーしたい場合は、 登録した input に type 属性を指定する必要があります。

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

Nota: multiple radio inputs with the same name, you want to register the validation to the last input so the hook understand validate them as a group at the end.

登録オプション

登録オプションを選択すると、下記の API テーブルが更新されます。

Name説明コードの例
ref
React.RefObject
React element ref
<input name="test" ref={register} />
required
boolean
Boolean の値。true の場合、フォームを送信する前に入力値が必須であることを示します。errors オブジェクトにエラーメッセージを返す文字列を割り当てることができます。
<input
  name="test"
  ref={
    register({
      required: true
    })
  }
/>
maxLength
number
input が受け付ける最大文字数。
<input
  name="test"
  ref={
    register({
      maxLength: 2
    })
  }
/>
minLength
number
input が取りうる妥当と判断される最小文字数。
<input
  name="test"
  ref={
    register({
      minLength: 1
    })
  }
/>
max
number
input が受け付ける最大数。
<input
  name="test"
  type="number"
  ref={
    register({
      max: 3
    })
  }
/>
min
number
input が取りうる妥当と判断される最小数。
<input
  name="test"
  type="number"
  ref={
    register({
      min: 3
    })
  }
/>
pattern
RegExp
input の内容が一致する必要がある正規表現。
<input
  name="test"
  ref={
    register({
      pattern: /[A-Za-z]{3}/
    })
  }
/>
validate
Function | Object
引数として、単一のバリデーションを行うためにはコールバック関数を渡すことができ、複数のバリデーションを行うためにはコールバック関数のオブジェクトを渡すことができます。 (例を参照)
<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

このメソッドでは、単一の input または input の配列に unregister を適用することができます。 これは、 useEffect でカスタム登録として input を登録 (register) し、 コンポーネントのアンマウント後に登録を解除する場合に便利です。

注意: input を登録解除 (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>

オブジェクトには、各 input のフォームのエラーまたはエラーメッセージが含まれています。

NameType説明
typestringError Type. eg: required, min, max, minLength
typesRecord<{ string, string | boolean }>これは、単一のフィールドで複数のエラーを返す必要がある、 パスワードのルールのような input のバリデーションに役立ちます。 この機能を有効にするには、 criteriaMode 'all' を設定してください。
messagestring | React.ReactElementメッセージはデフォルトでは空文字です。ただし、バリデーションとエラーメッセージで登録するとエラーメッセージが返されます。
refReact.Refinput 要素の参照。

注意: を使用すると、エラー状態の処理に役立ちます。

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

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: 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 { formState: { 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 { useForm } from "react-hook-form";

interface IFormInputs {
  password: string
}

export default function App() {
  const { register, formState: { 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

これは指定された input/inputs を監視し、その値を返します。

  • defaultValue が定義されていない場合、watch の初回のレンダリングは register の前に呼び出されるため undefined を返しますが、 第2引数として defaultValue を設定して値を返すことができます。

  • ただし、引数として useForm defaultValues が初期化された場合、 初回のレンダリングは defaultValues で指定された値を返します。

Type説明Return
stringinput の値を name から監視します (lodash の get 関数に似ています)watch('inputName')
watch('inputName', 'value')
any
string[]複数の input を監視しますwatch(['inputName1'])
watch(['field1'], { field1: '1' })
{ [key:string] : any }
undefined全ての input を監視しますwatch()
watch(undefined, { field: '1' })
{ [key:string] : any }
CodeSandbox JS
import React from "react";
import { useForm } from "react-hook-form";

function App() {
  const { register, watch, 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

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

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input type="checkbox" name="showAge" ref={register} />
        
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && <input type="number" name="age" ref={register({ 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, 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", "number"]); // you can also target specific fields by their names

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

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input
          name="name"
          ref={register({ required: true, maxLength: 50 })}
        />
        <input type="checkbox" name="showAge" ref={register} />
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && (
          <>
            <input type="number" name="age" ref={register({ 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}
            name={`test[${index}].firstName`}
            defaultValue={field.firstName}
            ref={register()}
          />
        );
      })}
      <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

この関数は、フォーム内のフィールドの値とエラーをリセット (reset) します。omitResetStateを指定することにより、次のことができます。 特定の状態のみをリセットします。 省略可能な引数として values を渡すと、 割り当てられたデフォルト値でフォームをリセットできます。

注意: ref を公開しない React-Select のような制御されたコンポーネントの場合は、 手動で入力値をリセットするために を使用するか、 コンポーネントを使用して制御されたコンポーネントをラップする必要があります。

注意: Controller コンポーネントの値をリセットするには、useFormdefaultValues を指定する必要があります。

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

単一または複数の input のエラーを手動で設定できます。

  • このメソッドは、入力された場合、関連する入力エラーを永続化しません 検証に合格します。

  • 入力フィールドに関連付けられていないエラーを設定します 永続化し、clearErrorsで手動で削除する必要があります。

  • handleSubmit関数の実行時に役立ちます。 非同期検証後にユーザーにエラーフィードバックを提供したい。

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

この関数を使用すると、input/select の値を動的に設定できます。 setValue は不要な再レンダリングを避けようとしますが、 以下の条件でのみ再レンダリングをトリガーします。

  • 値の更新によってエラーがトリガーされるとき

  • 値の更新によってエラーが修正されるとき

  • setValue が初めて実行され、 formState の isDirty が true に設定されたとき

注意: このメソッドを呼び出すことで、formState は input の name touched にプッシュします。

shouldValidatetrue に設定すると、 フィールドのバリデーションがトリガーされます。

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

フィールドをダーティに設定するために、shouldDirtyパラメータをtrueに設定することもできます。

setValue('name', 'value', { shouldDirty: true })
CodeSandbox JS
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():フォームの値全体を読み取ります。

  • getValues('test':個々の入力値を読み取りますname

  • getValues(['test', 'test1']):複数の入力を読み取りますname

CodeSandbox JS
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>

フォームで input/select のバリデーションを手動でトリガーします。

注意: バリデーションが失敗すると、errors オブジェクトが更新されます。

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 にラップされているので、状態の更新を有効にするには、render 前に formState を呼び出すか、読み込む必要があります。 この再レンダリング機能の削減機能は、Webプラットフォームにのみ適用されます。 React NativeでのProxyのサポートについて。

NameType説明
isDirtybooleanユーザが入力操作した後 true に設定します。
dirtyFieldsobjectユーザーが変更したフィールドの一意の Set オブジェクト。
touchedobject操作された全ての input の name の配列。
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) => 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 は、非制御コンポーネントとネイティブ HTML input をサポートしていますが、 React-Select AntD MUIなどの外部の制御された UI コンポーネントライブラリと組み合わせての使用​​を避けることは難しいです。 このラッパーコンポーネントにより、制御されたコンポーネントと組み合わせて簡単に使用​​できるようになります。

Controllerコンポーネントへ渡す全ての props は、as prop で指定した Component インスタンスに転送されます。 つまり、label prop を必要とするカスタム Switch コンポーネントがあるとします。 この label prop を Controller コンポーネントに直接渡すことができます。name prop は、主に後でフォームから値にアクセスするために使用されます。

NameType必須説明
namestringinput のユニークな name 。
controlObjectcontrol オブジェクトは useForm から呼び出されます。 ただし、 FormContext を使用している場合は省略できます。
asReact.ElementTypeコントローラーはonChangeonBlur を挿入しますvalueをコンポーネントにプロップします。
<Controller 
  as={<TextInput />} 
  control={control} 
  name="test" 
/>
<Controller 
  as={TextInput} 
  control={control} 
  name="test" 
/>
renderFunctionこれはレンダリングプロップ。 React要素を返し、次の機能を提供する関数 イベントと値をコンポーネントにアタッチします。これにより、 非標準の小道具で外部制御コンポーネントと統合する 名前:onChangeonBlurおよびvalue
<Controller
                  control={control}
                  name='test'
                  render={({ onChange, onBlur, value }) => (
                    <Input
                      onTextChange={onChange}
                      onTextBlur={onBlur}
                      textValue={value}
                    />
                  )}
                />
                <Controller render={props => <Input {...props} />} />
asReact.ElementType | string制御されたコンポーネント。 例: as="input" または as={<TextInput />}
defaultValueany非制御コンポーネントの defaultValue と同じですが、 boolean 値を指定すると、チェックボックスの input として扱われます。

注意: useFormdefaultValue または defaultValues のどちらかを指定する必要があります。

注意: フォーム内でデフォルト値を引数として reset を呼び出す場合、 インラインの defaultValue を設定する代わりに、 useForm で defaultValues を指定する必要があります。

rulesObjectregister によるバリデーションルール。
  • ローカル状態:更新された検証でregister入力 ルールまたはuseEffectでの入力をunregisterし、Controllerに更新されたrulesでそれ自体を再登録させます。
  • 入力状態:getValuesvalidate関数を活用して、条件付きで検証を返します。

register('name', { required: state })
validate: (value) => value === getValues('firstName');
onFocus() => void

このコールバック関数により、エラーが発生した場合にカスタムフックが入力フィールドにフォーカスさせることができます。 この関数は、React と React-Native どちらでも、フォーカスできるコンポーネントであれば適用可能です。

こちらの MUI を使用した実装例を参考にしてください。

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>
  );
}
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({
    defaultValues: {
      firstName: '',
      lastName: ''
    }
  });
  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"
      />
      {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"
      />

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

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

このカスタムフックは Controller を動かし、共有するものです。Controllerと同じプロップスやメソッドを使用しています。これは便利なことに 再利用可能なControlled入力を作成し、Controllerは ページやフォームにドロップするための柔軟なオプションです。

このフックの の引数を共有しています。as 以外は同じ引数を共有しています。レンダリング

React.RefObject
Object NameNameType説明
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

関連する input のエラーメッセージを表示するためのシンプルなコンポーネント。

npm install @hookform/error-message
NameType必須説明
namestring関連するフィールド名
errorsobjectReact Hook Form の errors オブジェクト
messagestring | React.ReactElementインラインエラーメッセージ。
asReact.ElementType | stringラッパーコンポーネント、または HTML タグ。 例: as="span" または as={<Text />}
render({ message: string | React.ReactElement, messages?: Object}) => anyこれは、単一または複数のエラーメッセージをレンダリングするための render prop です。

注意: messages を使用するためには、 criteriaMode を 'all' に設定する必要があります。

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();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="singleErrorInput" ref={register({ 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';

interface FormInputs {
  singleErrorInput: string
}

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="singleErrorInput" ref={register({ 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 {
  multipleErrorInput: string
}

export default function App() {
  const { register, formState: { 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"
        render={({ messages }) =>
          messages &&
          Object.entries(messages).map(([type, message]) => (
            <p key={type}>{message}</p>
          ))
        }
      />

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

useFormContext: Function

フォームコンテキストは、コンポーネントツリーに深くネストされた input があり、 メソッドを props として深く渡すことが苦痛になる場合の問題を解決することを目的としています。

Return

This hook will return all the useForm return methods and props.

const methods = useForm()
      
<FormProvider {...methods} /> // all the useForm return props
      
const methods = useFormContext() // retrieve those props

フォームが FormProvider でラップされると、 ラップされた子コンポーネントで useFormContext を使用して function を呼び出すことができます。

注意: useFormContext を呼び出すと全ての useForm フック関数が得られます。

Rules

You need to wrap your form with the FormProvider component for useFormContext to work properly.

Examples

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?: unknown, disabled?: boolean }) => object

watch APIと同じ機能を共有しますが、これは コンポーネントレベルで再レンダリングを分離し、結果的に アプリケーションのパフォーマンスが向上します。

Props

NameType説明
namestring | string[] | undefined関連するフィールド名
controlObjectcontrol オブジェクトは useForm から呼び出されます。 ただし、 FormContext を使用している場合は省略できます。
defaultValueunknown

default value for useWatch to return before the initial render.

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

disabledboolean = false

Option to disable the subscription.

exactboolean = false

This prop will enable an exact match for input name subscriptions.

Return

Return
useWatch('inputName')unknown
useWatch(['inputName1'])unknown[]
useWatch(){[key:string]: unknown}

Rules

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

  • The only difference between useWatch and watch is at the root (useForm) level or the custom hook level update.

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

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

Examples

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

function Child({ control }) {
  const firstName = useWatch({
    control,
    name: "firstName",
  });

  return <p>Watch: {firstName}</p>;
}

function App() {
  const { register, control } = useForm({
    firstName: "test"
  });
  
  return (
    <form>
      <input {...register("firstName")} />
      <Child control={control} />
    </form>
  );
}
import React from "react";
import { useForm, useWatch } from "react-hook-form";

interface FormInputs {
  firstName: string;
  lastName: 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 custom hook 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 {...register("lastName")} />
      <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

フィールド配列(動的な複数の input)を操作するためのカスタムフック。 このフックの背後にある動機は、より良いユーザーエクスペリエンスとフォームのパフォーマンスを提供することです。この短いビデオで、制御されたフィールド配列と非制御フィールド配列を比較できます。

NameType必須説明
namestring関連するフィールド名
controlObjectcontrol オブジェクトは useForm から呼び出されます。 ただし、 FormContext を使用している場合は省略できます。
keyNamestring = 'id'フィールド配列のkey値、デフォルトは「id」、次のことができます キー名を変更します。

このフックは、次のオブジェクトと関数を提供します。

function FieldArray() {
  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
  });

  return (
    {fields.map((field, index) => (
      <input
        key={field.id} // important to include key with field's id
        {...register(`test.${index}.value`)} 
      />
    ))}
  );
}

重要: useFieldArray は非制御コンポーネントに基づいて構築されます。 下記の注意事項は、実装時の動作を理解し、注意するのに役立ちます。

  • useForm フックで defaultValues を指定することにより、 fields 配列に値を格納することができます。

  • fields オブジェクトから id をコンポーネントの key に割り当てていることを確認してください。

  • 必ずdefaultValueをに設定してくださいfields[index]デフォルト値を設定する場合、 入力で削除またはリセットします。

  • 次々とアクションを呼び出すことはできません。アクションはレンダリングごとにトリガーする必要があります。

    // ❌ 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()} を適用して、 map 中に register が呼び出されるようにすることが重要です。

  • useEffectのカスタムレジスタでは機能しません。
NameType説明
fieldsobject & { id: string }このオブジェクトは、input をマップおよびレンダリングするための信頼できる情報源です。

重要: 各 input は制御することができないため、 マップされたコンポーネントには id が必須です。 これは、React が変更、追加もしくは削除されたのかを識別するのに役立ちます。

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

append(obj: object | object[], shouldFocus?: boolean = true) => voidフィールドの最後に input を追加します。
prepend(obj: object | object[], shouldFocus?: boolean = true) => voidフィールドの先頭に input を追加します。
insert(index: number, value: object, shouldFocus?: boolean = true) => void特定の位置に input を挿入します。
swap(from: number, to: number) => voidinput の位置を入れ替えます。
move(from: number, to: number) => voidinput を別の位置に移動します。

注意: move swap の違いは、move を呼び出し続けると input が円を描いて押し込まれるのに対して、swap は2つの input の位置を変更するだけです。

remove(index?: number | number[]) => void特定の位置の input を削除します。引数で index を渡さない場合は全ての input を削除します。
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({
    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