React Hook Form とは
React Hook Form は、React におけるフォームの状態管理とバリデーションを効率的に行うためのライブラリ。フォーム入力を useState で1つずつ管理する従来の方法では、入力のたびに再レンダーが発生し、フォームが大きくなるほどパフォーマンスが低下する問題があった。
import { useState } from "react";
function App() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [error, setError] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (!name) {
setError("名前は必須です");
return;
}
if (!email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
setError("メールアドレスの形式が正しくありません");
return;
}
setError("");
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<label>
名前:
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<label>
メールアドレス:
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
{error && <p style={{ color: "red" }}>{error}</p>}
<button type="submit">送信</button>
</form>
);
}
export default App;
React Hook Form は内部的に ref を使って値を管理し、再レンダーを最小限に抑えることでこの問題を解決している。
さらに、バリデーションを簡潔に記述できる点も特徴である。各フィールドを register で登録し、バリデーションルールを指定するだけで、未入力や形式エラーなどを自動的に検出できる。yup や zod といったスキーマバリデーションライブラリとの連携にも対応しており、Material UI などの UI フレームワークとも統合しやすい。
import { useForm } from "react-hook-form";
function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
名前:
<input {...register("name", { required: "名前は必須です" })} />
</label>
{errors.name && <p style={{ color: "red" }}>{errors.name.message}</p>}
<label>
メールアドレス:
<input
{...register("email", {
required: "メールアドレスは必須です",
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "メールアドレスの形式が正しくありません",
},
})}
/>
</label>
{errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}
<button type="submit">送信</button>
</form>
);
}
export default App;
つまり、React Hook Form はフォームに関する複雑な状態管理やバリデーション処理をシンプルにし、React アプリケーションのパフォーマンスと可読性を両立させるための仕組みである。
フィールドの登録
React Hook Form の基本となる考え方のひとつは、入力コンポーネントをフックに「登録(register)」すること。登録されたフィールドの値は、フォームのバリデーション(入力チェック)や送信処理で利用できるようになる。
import ReactDOM from "react-dom"
import { useForm, SubmitHandler } from "react-hook-form"
enum GenderEnum {
female = "female",
male = "male",
other = "other",
}
interface IFormInput {
firstName: string
gender: GenderEnum
}
export default function App() {
const { register, handleSubmit } = useForm<IFormInput>()
const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input {...register("firstName")} />
<label>Gender Selection</label>
<select {...register("gender")}>
<option value="female">female</option>
<option value="male">male</option>
<option value="other">other</option>
</select>
<input type="submit" />
</form>
)
}
この記述は、実際には以下のように展開されているのと同じ意味を持つ。
<input
name="firstName"
ref={...}
onChange={...}
onBlur={...}
/>
つまり、register は ref やイベントハンドラを自動で設定し、入力の値・フォーカス・バリデーションなどをReact Hook Form の内部状態に同期させる仕組み。
フィールドを登録するには、必ず name 属性 を指定する必要がある。
バリデーションの適用
React Hook Form は、既存の HTML 標準のフォームバリデーション仕様に沿って動作するため、
シンプルな記述で入力チェック(バリデーション)を実装できる。
| ルール名 | 説明 |
|---|---|
| required | 入力必須かどうかを指定する。 |
| min | 数値入力における最小値を指定する。 |
| max | 数値入力における最大値を指定する。 |
| minLength | 文字列の最小文字数を指定する。 |
| maxLength | 文字列の最大文字数を指定する。 |
| pattern | 正規表現パターンで入力内容をチェックする。 |
| validate | カスタム関数で自由にバリデーションを定義する。 |
import { useForm, SubmitHandler } from "react-hook-form"
interface IFormInput {
firstName: string
lastName: string
age: number
}
export default function App() {
const { register, handleSubmit } = useForm<IFormInput>()
const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName", { required: true, maxLength: 20 })} />
<input {...register("lastName", { pattern: /^[A-Za-z]+$/i })} />
<input type="number" {...register("age", { min: 18, max: 99 })} />
<input type="submit" />
</form>
)
}
既存フォームとの統合
既存のフォームコンポーネントを React Hook Form に組み込むのは基本的に簡単。
重要なのは、コンポーネントの ref を登録(register)し、必要なプロパティを入力要素に渡すこと。
import { Path, useForm, UseFormRegister, SubmitHandler } from "react-hook-form"
interface IFormValues {
"First Name": string
Age: number
}
type InputProps = {
label: Path<IFormValues>
register: UseFormRegister<IFormValues>
required: boolean
}
// The following component is an example of your existing Input Component
const Input = ({ label, register, required }: InputProps) => (
<>
<label>{label}</label>
<input {...register(label, { required })} />
</>
)
// you can use React.forwardRef to pass the ref too
const Select = React.forwardRef<
HTMLSelectElement,
{ label: string } & ReturnType<UseFormRegister<IFormValues>>
>(({ onChange, onBlur, name, label }, ref) => (
<>
<label>{label}</label>
<select name={name} ref={ref} onChange={onChange} onBlur={onBlur}>
<option value="20">20</option>
<option value="30">30</option>
</select>
</>
))
const App = () => {
const { register, handleSubmit } = useForm<IFormValues>()
const onSubmit: SubmitHandler<IFormValues> = (data) => {
alert(JSON.stringify(data))
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input label="First Name" register={register} required />
<Select label="Age" {...register("Age")} />
<input type="submit" />
</form>
)
}
UIライブラリとの統合
React Hook Form は、外部の UI コンポーネントライブラリとの統合を簡単にしている。コンポーネントが入力要素の ref を直接提供していない場合は、Controller コンポーネントを使うことで登録処理を自動的に行うことができる。
import Select from "react-select"
import { useForm, Controller, SubmitHandler } from "react-hook-form"
import { Input } from "@material-ui/core"
interface IFormInput {
firstName: string
lastName: string
iceCreamType: { label: string; value: string }
}
const App = () => {
const { control, handleSubmit } = useForm({
defaultValues: {
firstName: "",
lastName: "",
iceCreamType: {},
},
})
const onSubmit: SubmitHandler<IFormInput> = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="firstName"
control={control}
render={({ field }) => <Input {...field} />}
/>
<Controller
name="iceCreamType"
control={control}
render={({ field }) => (
<Select
{...field}
options={[
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
]}
/>
)}
/>
<input type="submit" />
</form>
)
}
制御された入力の統合
React Hook Form は基本的に制御されていないコンポーネントやネイティブ HTML 入力を推奨している。しかし、shadcn/ui、React-Select、AntD、MUI などの外部のコントロールされたコンポーネントを使わざるを得ない場合もある。そのような場合に簡単に統合できるよう、Controller コンポーネントが用意されており、登録処理を自動で行いつつ、カスタムの register を使う自由も提供している。
import { useForm, Controller, SubmitHandler } from "react-hook-form"
import { TextField, Checkbox } from "@material-ui/core"
interface IFormInputs {
TextField: string
MyCheckbox: boolean
}
function App() {
const { handleSubmit, control, reset } = useForm<IFormInputs>({
defaultValues: {
MyCheckbox: false,
},
})
const onSubmit: SubmitHandler<IFormInputs> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="MyCheckbox"
control={control}
rules={{ required: true }}
render={({ field }) => <Checkbox {...field} />}
/>
<input type="submit" />
</form>
)
}
import * as React from "react"
import { useForm, useController, UseControllerProps } from "react-hook-form"
type FormValues = {
FirstName: string
}
function Input(props: UseControllerProps<FormValues>) {
const { field, fieldState } = useController(props)
return (
<div>
<input {...field} placeholder={props.name} />
<p>{fieldState.isTouched && "Touched"}</p>
<p>{fieldState.isDirty && "Dirty"}</p>
<p>{fieldState.invalid ? "invalid" : "valid"}</p>
</div>
)
}
export default function App() {
const { handleSubmit, control } = useForm<FormValues>({
defaultValues: {
FirstName: "",
},
mode: "onChange",
})
const onSubmit = (data: FormValues) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input control={control} name="FirstName" rules={{ required: true }} />
<input type="submit" />
</form>
)
}
グローバルステートとの統合
React Hook Form は、必ずしも状態管理ライブラリに依存する必要はないが、必要に応じて簡単に統合することもできる。
import { useForm } from "react-hook-form"
import { connect } from "react-redux"
import updateAction from "./actions"
export default function App(props) {
const { register, handleSubmit, setValue } = useForm({
defaultValues: {
firstName: "",
lastName: "",
},
})
// Submit your data into Redux store
const onSubmit = (data) => props.updateAction(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<input type="submit" />
</form>
)
}
// Connect your component with redux
connect(
({ firstName, lastName }) => ({ firstName, lastName }),
updateAction
)(YourForm)
エラーの処理
React Hook Form では、フォーム内のエラーを確認できる errors オブジェクトが用意されている。errors の型は設定したバリデーションルールに応じて決まる。以下の例では、必須入力(required)のバリデーションルールを使った場合を示している。
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("firstName", { required: true })}
aria-invalid={errors.firstName ? "true" : "false"}
/>
{errors.firstName?.type === "required" && (
<p role="alert">First name is required</p>
)}
<input
{...register("mail", { required: "Email Address is required" })}
aria-invalid={errors.mail ? "true" : "false"}
/>
{errors.mail && <p role="alert">{errors.mail.message}</p>}
<input type="submit" />
</form>
)
}
外部サービスとの統合
React Hook Form を外部サービスと連携させるには、ライブラリに用意されている送信処理機能を利用できる。<Form /> コンポーネントを使うと、フォームのデータを簡単に API エンドポイントやその他のサービスに送信できる。
import { Form } from "react-hook-form"
function App() {
const { register, control } = useForm()
return (
<Form
action="/api/save" // Send post request with the FormData
// encType={'application/json'} you can also switch to json object
onSuccess={() => {
alert("Your application is updated.")
}}
onError={() => {
alert("Submission has failed.")
}}
control={control}
>
<input {...register("firstName", { required: true })} />
<input {...register("lastName", { required: true })} />
<button>Submit</button>
</Form>
)
}
スキーマバリデーション
React Hook Form では、Yup、Zod、Superstruct、Joi などのスキーマベースのバリデーションもサポートしている。作成したスキーマを useForm にオプションとして渡すと、入力データがスキーマに沿って検証され、エラー情報か有効な結果が返される。
import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"
const schema = yup
.object({
firstName: yup.string().required(),
age: yup.number().positive().integer().required(),
})
.required()
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
})
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} />
<p>{errors.firstName?.message}</p>
<input {...register("age")} />
<p>{errors.age?.message}</p>
<input type="submit" />
</form>
)
}
useForm
useForm はフォームを簡単に管理するためのカスタムフックで、オプションとして 1 つのオブジェクトを引数に取ることができる。以下の例では、useForm のすべてのプロパティとそれぞれのデフォルト値を示している。
| オプション名 | 説明 |
|---|---|
| mode | 送信前のバリデーション戦略を指定する。 |
| reValidateMode | 送信後のバリデーション戦略を指定する。 |
| defaultValues | フォームの初期値。キャッシュされる。 |
| values | リアクティブにフォームの値を更新する。 |
| errors | サーバーから返されたエラーでフォームを更新。 ⚠ 重要: 無限レンダリングを避けるため、errors オブジェクトの参照は安定させる。 |
| resetOptions | フォームの状態をリセットする際のオプション。 |
| criteriaMode | バリデーションエラーをすべて表示するか、1 つだけ表示するかを指定する。 |
| shouldFocusError | バリデーションエラー時の自動フォーカスを有効/無効にする。 |
| delayError | エラー表示を即時ではなく遅延させる。 |
| shouldUseNativeValidation | ブラウザ組み込みのフォーム制約 API を使用する。 |
| shouldUnregister | コンポーネントのアンマウント後に入力を自動的に登録解除するかを指定する。 |
| disabled | フォーム全体とすべての関連入力を無効化する。 |
register
register: (name: string, options?: RegisterOptions) => ({ ref, name, onChange, onBlur })
register を使うと、入力欄やセレクトボックスを React Hook Form に登録し、バリデーションルールを適用できる。バリデーションは HTML 標準に基づくものに加え、独自のカスタム検証も設定可能。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
入力要素の名前 |
| options | RegisterOptions |
入力の振る舞い(バリデーションなど) |
Return
| 名前 | 型 | 説明 |
|---|---|---|
| ref | React.ref |
フックフォームと入力を接続するための React 要素の ref |
| name | string |
登録される入力要素の名前 |
| onChange | ChangeHandler |
入力の変更イベントに対応する onChange プロパティ |
| onBlur | ChangeHandler |
入力のフォーカスアウトイベントに対応する onBlur プロパティ |
unregister
unregister: (name: string | string[], options) => void
unregister を使うと、単一の入力または複数の入力をフォームから登録解除できる。また、オプションの第2引数を指定することで、入力を登録解除した後も状態を保持することができる。
<input {...register('yourDetails.firstName')} />
<input {...register('yourDetails.lastName')} />
Props
| 型 | 入力名 | 値 |
|---|---|---|
string |
unregister("yourDetails") |
{} |
string |
unregister("yourDetails.firstName") |
{ lastName: '' } |
string[] |
unregister(["yourDetails.lastName"]) |
'' |
入力フィールドを削除したり、条件によって一時的にフォームから除外したいときに使う。そのフィールドの値は getValues() や watch() にも現れなくなる
formState
formState: Object
formState は、フォーム全体の状態に関する情報がまとまっている。ユーザーがフォームでどんな操作をしているかを把握し続けるために役立つ。
Return
| 名前 | 型 | 説明 |
|---|---|---|
| isDirty | boolean |
どれか1つでも入力値が初期値から変更されたら true。フォーム全体の「変更あり」。defaultValues を useForm で必ず設定する必要がある。 |
| dirtyFields | object |
ユーザーが変更した項目だけを記録したオブジェクト。フィールド単位の「変更あり」。isDirty とは目的が違う。 |
| touchedFields | object |
ユーザーが触った(フォーカスした/変更しようとした)項目の一覧。 |
| defaultValues | object |
useForm で指定した初期値、または reset で更新された初期値。状態比較の基準になる。 |
| isSubmitted | boolean |
フォームが一度でも送信されたら true(reset まで保持される)。 |
| isSubmitSuccessful | boolean |
エラーなく送信が成功したら true。 |
| isSubmitting | boolean |
送信中なら true。非同期処理中も true。 |
| isLoading | boolean |
defaultValues を async 関数で取得している時にロード中を表す。 |
| submitCount | number |
フォームを送信した回数。 |
| isValid | boolean |
フォーム全体にエラーがなければ true。setError の有無ではなく、最終的なバリデーション結果で決まる。 |
| isValidating | boolean |
バリデーション中なら true。 |
| validatingFields | object |
非同期バリデーション中のフィールド一覧。 |
| errors | object |
エラー内容の一覧。フィールドごとにエラー情報が入る。 |
| disabled | boolean |
useForm の disabled オプションによってフォーム全体が無効化されている場合 true。 |
| isReady | boolean |
formState の内部購読(subscription)が完了して、子コンポーネントでも安全に useForm のメソッドを使える状態。 |
watch
watch: UseFormWatch
watch は、指定した入力項目を監視し、その値を返す。入力値を画面に表示したり、条件に応じて描画内容を切り替えたい場合に便利。
Overloads
watch(name: string, defaultValue?: unknown): unknown
watch(names: string[], defaultValue?: {[key:string]: unknown}): unknown[]
watch(): {[key:string]: unknown}
単一フィールドを監視する watch(name: string, defaultValue?: unknown): unknown
レンダー関数の外で使われている単一フィールドを監視し、その変化を購読する。
Params
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
フィールド名 |
| defaultValue | unknown |
任意。フィールドの初期値 |
Returns
const name = watch("name")
複数フィールドを監視する watch(names: string[], defaultValue?: {[key:string]: unknown}): unknown[]
レンダー関数の外で使われている複数フィールド(配列)を監視し、その変化を購読する。
Params
| 名前 | 型 | 説明 |
|---|---|---|
| names | string[] |
フィールド名の配列 |
| defaultValue | {[key: string]: unknown} |
任意。フィールドの初期値をまとめたオブジェクト |
Returns
const [name, name1] = watch(["name", "name1"])
フォーム全体を監視する watch(): {[key:string]: unknown}
useForm 内で onChange をトリガーに、フォーム全体の更新や変更を監視・購読し、再レンダリングを行う。
Returns
const formValues = watch()
watch は フォームの値をリアルタイムで確認したいとき に使用する。
値を取得する方法としては getValues() などもあるが、フォームの値が変わるたびに自動で再レンダリングされれば問題はない。しかし、React Hook Form は 無駄な再レンダリングを抑える仕組み を持つため、getValues() で取得した値が常に最新とは限らない。そのため、入力中の最新値を確認したい場合は watch を使う必要がある。
const { register, getValues } = useForm();
const name = getValues("name"); // この時点の値しか取得できない
return (
<div>
<input {...register("name")} />
<p>こんにちは、{name}さん</p> {/* 入力中は更新されない */}
</div>
);
subscribe
subscribe: UseFormSubscribe<TFieldValues extends FieldValues>
フォームの状態や入力値の変更を購読できる。個別のフィールドだけ、またはフォーム全体を購読でき、フォームの変更による不要な再レンダリングを避けられる。
Props
| 名前 | 型 | 説明 | 例 |
|---|---|---|---|
| name | undefined / string[] |
購読対象を指定。未指定でフォーム全体を購読。複数フィールド名も指定可能 |
subscribe()subscribe({ name: ['firstName', 'lastName'] })
|
| formState | Partial<ReadFormState> |
購読したい formState の項目を選択 | subscribe({ formState: { values: true, isDirty: true, dirtyFields: true, touchedFields: true, isValid: true, errors: true, validatingFields: true, isValidating: true } }) |
| callback | Function |
購読時に呼ばれるコールバック関数 | subscribe({ formState: { values: true }, callback: ({ values }) => { console.log(values) } }) |
| exact | boolean |
input 名の購読を厳密一致にするかどうか | subscribe({ name: 'target', exact: true }) |
useEffect を使って一度購読登録をすれば、非同期でフォームの変更に応じて自動的にコールバックが実行される。ただし、購読解除を忘れないこと。
import { useForm } from "react-hook-form"
type FormInputs = {
firstName: string
lastName: string
}
export default function App() {
const { register, subscribe } = useForm<FormInputs>()
useEffect(() => {
// make sure to unsubscribe;
const callback = subscribe({
formState: {
values: true,
},
callback: ({ values }) => {
console.log(values)
},
})
return () => callback()
// You can also just return the subscribe
// return subscribe();
}, [subscribe])
return (
<form>
<input {...register("firstName", { required: true })} />
<input {...register("lastName", { required: true })} />
</form>
)
}
handleSubmit
</> handleSubmit: ((data: Object, e?: Event) => Promise<void>, (errors: Object, e?: Event) => Promise<void>) => Promise<void>
handleSubmit はフォームのバリデーションが成功した場合にフォームのデータを受け取る。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| SubmitHandler | (data: Object, e?: Event) => Promise<void> |
バリデーションが成功したときに呼ばれるコールバック |
| SubmitErrorHandler | (errors: Object, e?: Event) => Promise<void> |
バリデーションエラーが発生したときに呼ばれるコールバック |
handleSubmit() はフォーム送信時に必要な処理をまとめてやってくれる関数。内部で preventDefault() によるページリロード防止、入力値の取得、バリデーションの実行、成功/失敗時のコールバック呼び出しを自動で行う。これにより、自分で値取得やバリデーション処理を書く必要がなく、安全かつ簡潔にフォーム送信を扱える。
reset
reset: <T>(values?: T | ResetAction<T>, options?: Record<string, boolean>) => void
フォーム全体の状態、フィールドの参照、購読情報をまとめてリセットする関数。オプション引数を使うと、フォームの一部だけをリセットすることもできる。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| values | object | (values: Object) => Object |
リセットするフォーム値のオブジェクト。指定する場合は全ての defaultValues を渡すことが推奨。 |
| keepErrors | boolean |
エラー状態を保持する。ただし、ユーザー操作により保証されなくなる場合あり。 |
| keepDirty | boolean |
dirtyFields の状態を保持し、isDirty も一時的に現在の状態のまま。※フォーム入力値は反映されず、状態のみ保持。 |
| keepDirtyValues | boolean |
dirtyFields と isDirty を保持し、未変更のフィールドのみ最新のリセット値で更新。dirtyFields を反映させるには購読が必要。 |
| keepValues | boolean |
フォーム入力値を変更せずに保持。 |
| keepDefaultValues | boolean |
初期化時の defaultValues を保持。isDirty は新しい値と元の defaultValues を比較して再評価される。dirtyFields も同様に更新される。 |
| keepIsSubmitted | boolean |
isSubmitted の状態を保持。 |
| keepTouched | boolean |
isTouched の状態を保持。 |
| keepIsValid | boolean |
isValid を一時的に現在の状態のまま保持。 |
| keepSubmitCount | boolean |
submitCount の状態を保持。 |
基本的にはフォーム送信後の初期化処理として使われる。
resetField
resetField: (name: string, options?: Record<string, boolean | any>) => void
特定のフィールドの状態だけをリセットする。
Props
この関数を呼び出すと、
-
isValidのフォーム状態が再評価される -
isDirtyのフォーム状態が再評価される
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
登録済みのフィールド名 |
| options.keepError | boolean |
true にするとフィールドのエラー状態を保持する |
| options.keepDirty | boolean |
true にすると dirtyFields を保持する |
| options.keepTouched | boolean |
true にすると touchedFields 状態を保持する |
| defaultValue | unknown |
指定しない場合はフィールドが元の defaultValue に戻る。指定した場合はフィールドの値をその値に更新し、`defa |
全体をまとめてリセットするなら reset()、特定フィールドだけ更新したいときは resetField()を使う。
setError
setError: (name: string, error: FieldError, { shouldFocus?: boolean }) => void
setError は1つまたは複数のエラーを手動で設定することができる。
Props
整理すると以下の表になる。
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
入力フィールドの名前 |
| error | { type: string, message?: string, types: MultipleFieldErrors } |
エラーの種類とメッセージを設定 |
| config | { shouldFocus?: boolean } |
エラー設定時に入力フィールドにフォーカスを当てるかどうか。 ※ フィールドが登録されている場合のみ有効で、カスタム登録では動作しない |
clearErrors
clearErrors: (name?: string | string[]) => void
clearError はフォーム内のエラーを手動でクリアすることができる。
Props
| 型 | 説明 | 例 |
|---|---|---|
undefined |
全てのエラーを削除 | clearErrors() |
string |
単一のエラーを削除 | clearErrors("yourDetails.firstName") |
string[] |
複数のエラーを削除 | clearErrors(["yourDetails.lastName"]) |
setValue
setValue: (name: string, value: unknown, config?: SetValueConfig) => void
setValueは、登録済みのフィールドの値を動的に設定できる。同時に、バリデーションを実行したりフォームの状態を更新するオプションもあり、不必要な再レンダリングをなるべく避けるように設計されている。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
対象の単一フィールドまたはフィールド配列の名前 |
| value | unknown |
フィールドに設定する値。必須で undefined は不可 |
| options.shouldValidate | boolean |
フィールド単体のバリデーション状態(errors)やフォーム全体の有効性(isValid)を更新する。 指定したフィールドのみの touchedFields を更新する |
| options.shouldDirty | boolean |
フィールド単体やフォーム全体の dirty 状態(dirtyFields / isDirty)を更新する。指定したフィールドのみの dirtyFields を更新 |
| options.shouldTouch | boolean |
フィールド自体を touched としてマークする |
setFocus
setFocus: (name: string, options: SetFocusOptions) => void
setFocus を使うと、プログラムから指定の入力フィールドにフォーカスを当てることができる。ただし、入力フィールドの ref が React Hook Form に登録されている必要がある。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
フォーカスを当てる入力フィールドの名前 |
| options.shouldSelect | boolean |
フォーカス時に入力内容を選択するかどうか |
getValues
getValues: (payload?: string | string[]) => Object
getValues はフォームの値を取得するための最適化されたヘルパー関数。watch と違い、getValues は再レンダリングを引き起こさず、入力の変更に購読も行わない。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| fieldNames | undefined |
フォーム全体の値を取得 (object) |
| fieldNames | string |
指定したパスの値を取得 (unknown) |
| fieldNames | string[] |
指定したパスの値を配列で取得 (unknown[]) |
| config.dirtyFields | boolean |
dirtyFields のみを取得 |
| config.touchedFields | boolean |
touchedFields のみを取得 |
getFieldState
getFieldState: (name: string, formState?: Object) => ({isDirty, isTouched, invalid, error})
getFieldState は個別のフィールド状態を取得できる。型安全にネストしたフィールドの状態を取得したい場合に便利。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| name | string |
登録済みのフィールド名 |
| formState | object |
オプション。useForm、useFormContext、useFormState から formState を読み取っていない場合に指定が必要。詳細はルールを参照 |
Return
| 名前 | 型 | 説明 | 条件 |
|---|---|---|---|
| isDirty | boolean |
フィールドが変更されたかどうか |
dirtyFields を購読していること |
| isTouched | boolean |
フィールドがフォーカス・ブラーされたかどうか |
touchedFields を購読していること |
| invalid | boolean |
フィールドが有効でないかどうか |
errors を購読していること |
| error | undefined | FieldError |
フィールドのエラーオブジェクト |
errors を購読していること |
getFieldState の引数 formState は、useForm、useFormContext、useFormState などを使用していない場合にのみ指定するもの。通常は指定不要。
戻り値の hogehoge を購読するには、formState や useFormState を使って、dirtyFields、touchedFields、errors など必要なプロパティにアクセスすると、その状態が購読される。
const { register, formState } = useForm();
const { dirtyFields, touchedFields, errors } = formState;
const { dirtyFields, touchedFields, errors } = useFormState({
name: "yourDetails.firstName" // 特定フィールドだけ購読可能
});
trigger
trigger: (name?: string | string[]) => Promise<boolean>
trigger を使うことで、フォームや入力フィールドのバリデーションを手動で実行できる。また、入力のバリデーションが他の入力値に依存している場合にも便利。
Props
| 名前 | 型 | 説明 | 例 |
|---|---|---|---|
| name | undefined |
全フィールドのバリデーションを実行 | trigger() |
| name | string |
指定したフィールドのバリデーションを実行 | trigger("yourDetails.firstName") |
| name | string[] |
複数フィールドのバリデーションを実行 | trigger(["yourDetails.lastName"]) |
| shouldFocus | boolean |
バリデーション時に入力にフォーカスするか。入力が登録されている場合のみ有効 | trigger('name', { shouldFocus: true }) |
control
control: Object
このオブジェクトには、コンポーネントを React Hook Form に登録するためのメソッドが含まれている。
control はフォームフィールドの状態管理の中心。値の取得・更新・バリデーション・購読 などを内部で一括管理している。普通の register は単純な input で使えるが、カスタムコンポーネントや複雑な UI では control を使うことになる
useController
useController:
(props?: UseControllerProps) => { field: object, fieldState: object, formState: object }
このカスタムフックは Controller と同じ仕組みで動くフックで、同じプロパティやメソッドが使える。つまり、このフックを使うと、何度も使える『値を管理できる入力フォーム』を簡単に作れる。
Props
| 名前 | 型 | 必須 | 説明 |
|---|---|---|---|
| name | FieldPath |
✓ | 入力フィールドの一意な名前 |
| control | Control |
useForm を呼び出したときに得られるコントロールオブジェクト。FormProvider 使用時は省略可能 |
|
| rules | Object |
register と同じ形式でバリデーションルールを指定できる。例: required, min, max, minLength, maxLength, pattern, validate
|
|
| shouldUnregister | boolean |
デフォルト値 false。アンマウント時に入力を登録解除し、defaultValues も削除する。※ useFieldArray と併用する場合は注意(アンマウント・再マウント後に unregister が呼ばれるため) |
|
| disabled | boolean |
デフォルト値 false。field から返される disabled。コントロールされた入力が無効化され、送信データから値が除外される |
|
| defaultValue | unknown |
useForm の defaultValues またはフィールド単位で指定する必要がある。undefined は無効。フォームで reset を使う場合は useForm に defaultValues を渡す必要がある |
Return
| オブジェクト | プロパティ | 型 | 説明 |
|---|---|---|---|
| field | onChange | (value: any) => void |
入力値をライブラリに送る関数。入力コンポーネントの onChange に割り当てる。value は undefined にしてはいけない。手動で setValue などを呼ぶ必要はない。 |
| field | onBlur | () => void |
入力の onBlur イベントをライブラリに送る関数。入力コンポーネントの onBlur に割り当てる。 |
| field | value | unknown |
コントロールされたコンポーネントの現在の値。 |
| field | disabled | boolean |
入力の無効状態。 |
| field | name | string |
登録された入力の名前。 |
| field | ref | React.Ref |
入力に ref を渡して Hook Form に接続。エラー入力にフォーカス可能にする。 |
| fieldState | invalid | boolean |
現在の入力が無効かどうか。 |
| fieldState | isTouched | boolean |
入力が触れられたかどうか。 |
| fieldState | isDirty | boolean |
入力が変更されたかどうか。 |
| fieldState | error | object |
この入力のエラー情報。 |
| formState | isDirty | boolean |
ユーザーが入力を変更した後に true になる。全フィールドのデフォルト値を useForm で設定しておく必要あり。 |
| formState | dirtyFields | object |
ユーザーが変更したフィールドのオブジェクト。全フィールドの defaultValues が必要。 |
| formState | touchedFields | object |
ユーザーが触れた入力フィールドのオブジェクト。 |
| formState | defaultValues | object |
useForm で設定したデフォルト値、または reset で更新した値。 |
| formState | isSubmitted | boolean |
フォームが送信された後に true になる。reset 呼び出しまで保持。 |
| formState | isSubmitSuccessful | boolean |
フォームがランタイムエラーなく正常に送信されたか。 |
| formState | isSubmitting | boolean |
現在フォームが送信中かどうか。 |
| formState | isLoading | boolean |
非同期 defaultValues をロード中かどうか。 |
| formState | submitCount | number |
フォームが送信された回数。 |
| formState | isValid | boolean |
フォームにエラーがない場合 true。setError は影響しない。 |
| formState | isValidating | boolean |
バリデーション中に true。 |
| formState | validatingFields | object |
非同期バリデーション中のフィールド情報。 |
| formState | errors | object |
フィールドごとのエラー情報。ErrorMessage コンポーネントでも取得可能。 |
| formState | disabled | boolean |
useForm の disabled プロパティでフォーム全体を無効化した場合に true。 |
useController は React Hook Form で カスタム入力コンポーネントをフォームに接続するためのフック。通常の register は HTML 標準の入力向けで、値の登録しかできないが、useController は入力値・フォーカス状態・バリデーション状態を細かく管理できる。
これにより、Material-UI(MUI)や shadcn/ui のようなカスタムコンポーネントでも、内部の ref や onChange を意識せずにフォームと同期できる。例えば MUI の TextField に field を渡すだけで、値の変更やエラー表示、フォーカス制御が自動で行われる。
import { TextField } from "@material-ui/core";
import { useController, useForm } from "react-hook-form";
function Input({ control, name }) {
const {
field,
fieldState: { invalid, isTouched, isDirty },
formState: { touchedFields, dirtyFields }
} = useController({
name,
control,
rules: { required: true },
});
return (
<TextField
onChange={field.onChange} // send value to hook form
onBlur={field.onBlur} // notify when input is touched/blur
value={field.value} // input value
name={field.name} // send down the input name
inputRef={field.ref} // send input ref, so we can focus on input when error appear
/>
);
}
まとめると、useController は 複雑なカスタム入力でも簡単に React Hook Form に接続できる仕組みで、UI ライブラリを安全かつ効率的に使うための必須ツールになる。
Controller
Controller: Component
React Hook Form は基本的にアンコントロールコンポーネント(ネイティブ入力)を前提に設計されているが、実際には React-Select や AntD、MUI などの外部のコントロールコンポーネント を使わざるを得ないことが多い。このラッパーコンポーネントを使うと、そうした外部コンポーネントと React Hook Form を 簡単に組み合わせて使えるようになる。
Props
| 名前 | 型 | 必須 | 説明 |
|---|---|---|---|
| name | FieldPath |
✓ | 入力フィールドの一意な名前 |
| control | Control |
useForm から取得するコントロールオブジェクト。FormProvider 使用時は任意 |
|
| render | Function |
レンダープロップ。React 要素を返し、onChange、onBlur、name、ref、value を子コンポーネントに渡せる。外部のコントロールコンポーネントとの統合を簡単にする。fieldState も提供される |
|
| rules | Object |
バリデーションルール。register と同じ形式で指定可能(required、min、max、minLength、maxLength、pattern、validate など) |
|
| shouldUnregister | boolean = false |
アンマウント時に入力を登録解除し、defaultValues も削除する。useFieldArray 使用時は注意(再マウント・並び替えで unregister が呼ばれるため) |
|
| disabled | boolean = false |
field から返される。コントロール入力が無効化され、送信データから値が除外される |
|
| defaultValue | unknown |
undefined は使用不可。フィールドレベルで設定するか、useForm の defaultValues を使用する必要がある。リセット時も defaultValues を渡す必要がある。onChange に undefined を渡すのは不可で、代わりに null または空文字を使用する |
Return
| オブジェクト | プロパティ | 型 | 説明 |
|---|---|---|---|
field |
onChange |
(value: any) => void |
入力値を React Hook Form に送る関数。onChange に割り当て、値は undefined にしない。フォーム状態を更新するので、setValue などを手動で呼ぶ必要はない |
field |
onBlur |
() => void |
入力の onBlur イベントを RHF に送る関数。onBlur に割り当てる |
field |
value |
unknown |
コントロールされたコンポーネントの現在の値 |
field |
disabled |
boolean |
入力の無効状態 |
field |
name |
string |
登録された入力の名前 |
field |
ref |
React.ref |
入力とフォームを接続するための ref。エラー時にフォーカスを当てるために割り当てる |
fieldState |
invalid |
boolean |
現在の入力が無効かどうか |
fieldState |
isTouched |
boolean |
現在の入力がタッチされたかどうか |
fieldState |
isDirty |
boolean |
現在の入力が変更されたかどうか |
fieldState |
error |
object |
この入力特有のエラー情報 |
formState |
isDirty |
boolean |
ユーザーがフォームのどれかの入力を変更したかどうか。全入力の defaultValues を useForm に渡すことが重要 |
formState |
dirtyFields |
object |
ユーザーが変更したフィールドのオブジェクト。useForm に全入力の defaultValues を渡すことが重要 |
formState |
touchedFields |
object |
ユーザーが操作した全入力のオブジェクト |
formState |
defaultValues |
object |
useForm の defaultValues または reset による更新値 |
formState |
isSubmitted |
boolean |
フォームが送信された後に true になる。reset 呼び出しまで保持 |
formState |
isSubmitSuccessful |
boolean |
フォームがランタイムエラーなしで正常に送信されたかどうか |
formState |
isSubmitting |
boolean |
現在フォームが送信中かどうか |
formState |
isLoading |
boolean |
非同期の defaultValues を読み込んでいる場合 true |
formState |
submitCount |
number |
フォームが送信された回数 |
formState |
isValid |
boolean |
フォームにエラーがない場合 true。setError は影響しない |
formState |
isValidating |
boolean |
バリデーション中に true |
formState |
errors |
object |
フィールドごとのエラーオブジェクト。ErrorMessage コンポーネントで簡単に取得可能 |
useController はフックで、自分で field を受け取り JSX に値やイベントを適用するための柔軟な方法。Controller はコンポーネントで、render プロップに入力を渡すだけで簡単に接続できる。要するに、細かく制御したいときは useController、手軽に接続したいときは Controller を使う。
useFormContext
useFormContext: Function
useFormContext はフォームのコンテキストにアクセスするためのものであり、コンテキストを毎回プロップとして渡すのが面倒になるような 深くネストされたコンポーネント構造 で使うことを想定している。
import { useForm, FormProvider, useFormContext } from "react-hook-form"
export default function App() {
const methods = useForm()
const onSubmit = (data) => console.log(data)
const { register, reset } = methods
useEffect(() => {
reset({
name: "data",
})
}, [reset]) // ❌ never put `methods` as the deps
return (
<FormProvider {...methods}>
// pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input {...register("name")} />
<input type="submit" />
</form>
</FormProvider>
)
}
function NestedInput() {
const { register } = useFormContext() // retrieve all hook methods
return <input {...register("test")} />
}
FormProvider
FormProvider: Component
FormProvider はフォームのコンテキストオブジェクトを保持し、子コンポーネントがそのコンテキストを利用して useForm のプロップやメソッドにアクセスできるようにする。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| ...props | Object |
FormProvider には useForm のすべてのメソッドが必要 |
FormProvider がコンテキストを渡し、useFormContext がそれを受け取って使う
useWatch
useWatch: ({ control?: Control, name?: string, defaultValue?: unknown, disabled?: boolean }) => object
watch API と似た動作をするが、このカスタムフックでは 再レンダリングをフック単位で分離 できる。そのため、アプリケーションのパフォーマンスが向上する可能性がある。
Props
| 名前 | 型 | 説明 |
|---|---|---|
name |
string | string[] | undefined |
監視するフィールドの名前 |
control |
Object |
useForm から提供されるコントロールオブジェクト。FormProvider 使用時は任意 |
compute |
function |
選択的・計算されたフォーム値を監視できる関数。フォーム全体を監視して特定条件で値を返したり、特定フィールドだけを監視可能。 例1: 全体を監視して条件付きで値を返す 例2: 特定フィールドだけを監視して計算して返す |
defaultValue |
unknown |
初回レンダー前に useWatch が返すデフォルト値。指定すると最初のレンダーでは必ずこの値が返る |
disabled |
boolean = false |
監視を無効化するオプション |
exact |
boolean = false |
入力名の完全一致で監視するかどうか |
Return
| 例 | 戻り値 |
|---|---|
useWatch({ name: 'inputName' }) |
unknown |
useWatch({ name: ['inputName1'] }) |
unknown[] |
useWatch() |
{ [key: string]: unknown } |
watch は値が変わると その値を使うコンポーネント全体が再レンダリング される。一方、useWatch は 監視する値だけを別コンポーネントやフックで管理するため、その値を使う部分だけが更新され、フォーム全体は再描画されない。パフォーマンスを意識した効率的な監視方法と言える。
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>
)
}
Watch
Watch: Component
useWatch と同じ機能を持つ コンポーネント版の React Hook Form。フックを別のコンポーネント内で使う代わりに、JSX 内で直接 <Watch /> を使うことで、フォームの値を監視して表示できる。
Props
| 名前 | 型 | 説明 |
|---|---|---|
name |
string | string[] | undefined |
監視するフィールドの名前 |
control |
Object |
useForm から提供されるコントロールオブジェクト。FormProvider 使用時は任意 |
compute |
function |
選択的・計算されたフォーム値を監視できる関数。 フォーム全体を監視して条件付きで値を返したり、特定フィールドだけを監視可能 |
defaultValue |
unknown |
初回レンダー前に返すデフォルト値。指定すると最初のレンダーでは必ずこの値が返る |
disabled |
boolean = false |
監視を無効化するオプション |
exact |
boolean = false |
入力名の完全一致で監視するかどうか |
render |
Function |
指定したフィールドを監視し、値が変わるたびに子関数を再レンダリングする。JSX 内で宣言的にフォーム値を利用できる |
useFormState
useFormState: ({ control: Control }) => FormState
useFormState は、フォームの状態ごとに購読でき、再レンダリングをそのカスタムフックの範囲だけに限定できる。購読するフォーム状態のスコープが明確なため、他の useFormState や useForm に影響を与えない。
複雑で大規模なフォームアプリケーションにおいて、再レンダリングの負荷を抑えるのに役立つ。
Props
| 名前 | 型 | 説明 |
|---|---|---|
| control | Object |
useForm が提供する control オブジェクト。FormProvider を使用している場合は省略可能。 |
| name | string / string[] |
特定のフィールド名、または複数のフィールド名を指定できる。未指定の場合は全フィールドの formState 更新を購読する。 |
| disabled | boolean = false |
購読を無効にするためのオプション。 |
| exact | boolean = false |
指定したフィールド名との一致を厳密に判定するオプション。 |
Returns
| 名前 | 型 | 説明 |
|---|---|---|
| isDirty | boolean |
ユーザーがいずれかの入力を変更した後に true になる。useForm で全フィールドの defaultValues を提供する必要がある。例:setValue('test', 'change') で true、setValue('test', '') では false。ファイル入力はキャンセル可能なためアプリ側で管理が必要。カスタムオブジェクトや Class、File オブジェクトはサポートされない。 |
| dirtyFields | object |
ユーザーが変更したフィールドのオブジェクト。全フィールドの defaultValues が必要。isDirty とは異なり、フィールド単位での状態を示す。 |
| touchedFields | object |
ユーザーが操作したすべての入力フィールドのオブジェクト。 |
| defaultValues | object |
useForm の defaultValues に設定された値、または reset API によって更新された値。 |
| isSubmitted | boolean |
フォーム送信後に true になり、reset が呼ばれるまで保持される。 |
| isSubmitSuccessful | boolean |
フォームがランタイムエラーなく正常に送信されたかを示す。 |
| isSubmitting | boolean |
現在フォームが送信中の場合は true、それ以外は false。 |
| isLoading | boolean |
現在フォームが非同期の defaultValues を読み込み中の場合に true。例:defaultValues: async () => await fetch('/api')
|
| submitCount | number |
フォームが送信された回数。 |
| isValid | boolean |
フォームにエラーがない場合に true。setError では影響を受けず、常にバリデーション結果から算出される。 |
| isValidating | boolean |
バリデーション中は true。 |
| validatingFields | object |
非同期バリデーション中のフィールドを示す。 |
| errors | object |
フィールドごとのエラーを格納するオブジェクト。ErrorMessage コンポーネントで簡単に取得可能。 |
| disabled | boolean |
useForm の disabled プロパティでフォームが無効化されている場合は true。 |
React Hook Form では formState が内部的に React の state として管理されているため、どこかの入力値が変わると formState を参照しているコンポーネント全体が再レンダリングされる。この挙動は小さなフォームでは問題にならないが、フィールドが多い大規模フォームではパフォーマンスに影響する。useFormState は特定のフィールドや状態だけを購読する仕組みを提供しており、必要な部分だけを再レンダリングできるため、効率的にフォーム状態を管理できる。
import { useForm, useFormState } from "react-hook-form"
function Child({ control }) {
const { dirtyFields } = useFormState({ control })
return dirtyFields.firstName ? <p>Field is dirty.</p> : null
}
export default function App() {
const { register, handleSubmit, control } = useForm({
defaultValues: {
firstName: "firstName",
},
})
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} placeholder="First Name" />
<Child control={control} />
<input type="submit" />
</form>
)
}
ErrorMessage
ErrorMessage: Component
関連する入力フィールドのエラーメッセージを表示するシンプルなコンポーネント
Props
| 名前 | 型 | 必須 | 説明 |
|---|---|---|---|
| name | string |
✓ | フィールド名 |
| errors | object |
React Hook Form のエラーオブジェクト。FormProvider を使用している場合は省略可能 |
|
| message | string | React.ReactElement |
インラインで表示するエラーメッセージ | |
| as | React.ElementType | string |
エラーメッセージのラッパーコンポーネントや HTML タグ。例:as="span" や `as={}。 |
|
| render | ({ message: string | React.ReactElement, messages?: Object }) => any |
エラーメッセージや複数メッセージを描画するための render prop。criteriaMode を 'all' に設定する必要がある |
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 { 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>
)
}
useFieldArray
useFieldArray: UseFieldArrayProps
フィールド配列(動的フォーム)を扱うためのカスタムフック。ユーザー体験とパフォーマンスを向上させることを目的としている。
Props
| 名前 | 型 | 必須 | 説明 |
|---|---|---|---|
| name | string |
✓ | フィールド配列の名前。動的な名前はサポートされない。 |
| control | Object |
useForm から提供されるコントロールオブジェクト。FormProvider を使用している場合は省略可能。 |
|
| shouldUnregister | boolean |
コンポーネントがアンマウントされたときにフィールド配列を登録解除するかどうか。 | |
| keyName | string |
自動生成された識別子を key プロップとして使う際の属性名。デフォルトは id。次のメジャーバージョンで廃止予定。 |
|
| rules | Object |
register と同じバリデーションルール API(required、minLength、maxLength、validate)を使用可能。組み込みバリデーションの場合、エラーは formState.errors?.fieldArray?.root に FieldError 型で追加される。例:useFieldArray({ rules: { minLength: 4 } })
|
Return
| 名前 | 型 / シグネチャ | 説明 |
|---|---|---|
| fields | object & { id: string } |
コンポーネントの defaultValue と key を含むオブジェクト。 |
| append | (obj: object | object[], focusOptions) => void |
フィールドの末尾に入力を追加してフォーカス。追加時に値は自動登録される。データは必須で部分的では不可。 |
| prepend | (obj: object | object[], focusOptions) => void |
フィールドの先頭に入力を追加してフォーカス。追加時に値は自動登録される。データは必須で部分的では不可。 |
| insert | (index: number, value: object | object[], focusOptions) => void |
指定位置に入力を挿入してフォーカス。データは必須で部分的では不可。 |
| swap | (from: number, to: number) => void |
フィールドの位置を入れ替える。 |
| move | (from: number, to: number) => void |
フィールドを別の位置に移動する。 |
| update | (index: number, obj: object) => void |
指定位置の入力を更新。更新されたフィールドはアンマウント・再マウントされる。不要な場合は setValue API を使用。データは必須で部分的では不可。 |
| replace | (obj: object[]) => void |
フィールド配列全体の値を置き換える。 |
| remove | (index?: number | number[]) => void |
指定位置の入力を削除。インデックス未指定の場合は全削除。 |
useFieldArray は React Hook Form で配列型の入力を動的に管理するためのフックで、行の追加・削除・並び替えやバリデーションを簡単に扱える。例えばユーザーリストのフォームでは、fields で現在の配列を取得し、append で新しい行を追加、remove で削除できる。各入力は register で配列のインデックスを指定して登録することで、送信時にまとめて取得できる。
const { control, register, handleSubmit } = useForm({ defaultValues: { users: [{ name: "", age: 0 }] } });
const { fields, append, remove } = useFieldArray({ name: "users", control });
<form onSubmit={handleSubmit(data => console.log(data))}>
{fields.map((field, i) => (
<div key={field.id}>
<input {...register(`users.${i}.name`)} placeholder="Name" />
<input {...register(`users.${i}.age`)} placeholder="Age" />
<button type="button" onClick={() => remove(i)}>削除</button>
</div>
))}
<button type="button" onClick={() => append({ name: "", age: 0 })}>追加</button>
<button type="submit">送信</button>
</form>