はじめに
React Hook Formとは
React Hook Formは、Reactでフォームを簡単に実装できるライブラリです。フォームの状態管理、バリデーション、エラー表示などを最小限のコードで実現できます。
従来のフォーム実装との違い
通常のReactでフォームを作成する場合、各入力フィールドに対してstateを定義し、onChange関数を実装する必要があります。しかし、React Hook Formを使用すると、これらの処理をライブラリが自動的に管理してくれるため、コード量を大幅に削減できます。
従来の実装では再レンダリングが頻繁に発生しますが、React Hook Formは非制御コンポーネントの仕組みを活用することで、パフォーマンスの向上も実現しています。
React Hook Formの基本的な使い方
インストール方法
npmまたはyarnを使用してインストールします。
npm install react-hook-form
最小構成のサンプルコード
以下は、商品名と数量を入力するシンプルなフォームの実装例です。
import { useForm } from 'react-hook-form';
function RHFForm({ addItem }) {
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
defaultValues: {
product: '',
quantity: 0,
},
});
const onSubmit = (data) => {
console.log(data);
addItem(data);
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="product">商品名</label>
<input
id="product"
type="text"
{...register('product', { required: '商品名は空にできません' })}
/>
{errors.product && errors.product.message}
</div>
<div>
<label htmlFor="quantity">数量</label>
<input
id="quantity"
{...register('quantity', {
required: '数量を入力してください',
valueAsNumber: true,
min: { value: 1, message: '1以上を入力してください' },
})}
/>
{errors.quantity && errors.quantity.message}
</div>
<input type="submit" />
</form>
);
}
export default RHFForm;
useFormフックで取得できる主な機能
useFormフックは、フォーム管理に必要な機能をまとめて提供します。分割代入で必要なものだけを取り出して使用します。
register(入力フィールドの登録)
入力フィールドをReact Hook Formに登録するための関数です。この関数を使用することで、各フィールドの値を自動的に管理し、バリデーションルールを設定できます。
handleSubmit(送信処理のラップ)
フォーム送信時のハンドラーをラップする関数です。送信前に自動的にバリデーションを実行し、エラーがなければ指定したコールバック関数を呼び出します。
formState(フォームの状態管理)
フォームの状態を含むオブジェクトです。errorsプロパティを使用することで、バリデーションエラー情報にアクセスできます。
reset(フォームのリセット)
フォームの値を初期値にリセットする関数です。送信後にフォームをクリアする場合などに使用します。
defaultValues(初期値の設定)
useFormのオプションとして渡すことで、フォームの初期値を設定できます。この値は、reset()を呼び出したときにフォームが戻る値にもなります。
useForm({
defaultValues: {
product: '',
quantity: 0,
},
});
registerによる入力フィールドの管理
registerの基本的な使い方
register関数は、第1引数にフィールド名、第2引数にバリデーションルールを受け取ります。
register('product', { required: '商品名は空にできません' })
この関数は、以下のようなオブジェクトを返します。
{
name: 'product',
onChange: [関数],
onBlur: [関数],
ref: [関数]
}
スプレッド構文での展開
registerが返すオブジェクトをスプレッド構文で展開することで、必要なpropsをinput要素に一括で渡せます。
<input {...register('product', { required: '商品名は空にできません' })} />
これは以下のコードと同じ意味になります。
<input
name="product"
onChange={onChangeFunction}
onBlur={onBlurFunction}
ref={refFunction}
/>
バリデーションルールの設定
registerの第2引数にオブジェクト形式でバリデーションルールを指定します。複数のルールを組み合わせることも可能です。
register('quantity', {
required: '数量を入力してください',
valueAsNumber: true,
min: { value: 1, message: '1以上を入力してください' },
})
バリデーションの実装
React Hook Formは、様々なバリデーションルールを簡単に設定できます。
必須入力チェック(required)
最も基本的なバリデーションです。フィールドが空の場合にエラーを表示します。
register('product', { required: '商品名は空にできません' })
真偽値だけを指定することもできますが、エラーメッセージを文字列で指定するのが一般的です。
数値変換(valueAsNumber)
input要素の値は通常文字列として扱われますが、valueAsNumber: trueを設定することで自動的に数値に変換されます。
register('quantity', {
valueAsNumber: true,
})
これにより、送信されるデータのquantityプロパティが文字列の"5"ではなく、数値の5になります。
最小値チェック(min)
数値フィールドに対して最小値を設定できます。
register('quantity', {
min: { value: 1, message: '1以上を入力してください' },
})
その他のバリデーションルール
React Hook Formは、他にも以下のようなバリデーションルールを提供しています。
-
max: 最大値のチェック -
minLength: 最小文字数のチェック -
maxLength: 最大文字数のチェック -
pattern: 正規表現によるパターンマッチング -
validate: カスタムバリデーション関数
register('email', {
required: 'メールアドレスは必須です',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: '有効なメールアドレスを入力してください',
},
})
エラーメッセージの表示
errorsオブジェクトの使い方
formStateから取得できるerrorsオブジェクトには、各フィールドのバリデーションエラー情報が格納されています。エラーが発生していないフィールドはundefinedになります。
const { formState: { errors } } = useForm();
各フィールドのエラーにアクセスするには、フィールド名をキーとして使用します。
errors.product // 商品名フィールドのエラー
errors.quantity // 数量フィールドのエラー
条件付きレンダリング
エラーメッセージは、エラーが存在する場合のみ表示するのが一般的です。論理AND演算子を使用した条件付きレンダリングで実装します。
{errors.product && errors.product.message}
これは以下のように動作します。
-
errors.productがundefined(エラーなし)の場合、何も表示されない -
errors.productが存在する(エラーあり)の場合、errors.product.messageの内容が表示される
より明示的に書く場合は、三項演算子を使用することもできます。
{errors.product ? <span>{errors.product.message}</span> : null}
フォーム送信処理の流れ
フォームの送信処理は、複数のステップを経て実行されます。Mermaid図で全体の流れを確認しましょう。
handleSubmitの役割
handleSubmitは、フォーム送信処理のゲートキーパーとして機能します。
<form onSubmit={handleSubmit(onSubmit)}>
この関数は以下の処理を自動的に実行します。
- フォームのデフォルト動作(ページリロード)を防ぐ
- 全フィールドのバリデーションを実行
- バリデーションが成功した場合のみ、引数で渡された関数を呼び出す
- バリデーションが失敗した場合、
errorsオブジェクトを更新
onSubmit関数の実装
onSubmit関数は、バリデーションが成功した場合のみ実行されます。引数のdataには、全フィールドの値がオブジェクトとして渡されます。
const onSubmit = (data) => {
console.log(data); // { product: '商品名', quantity: 5 }
addItem(data);
reset();
};
dataオブジェクトの構造は、registerで登録したフィールド名がキーとなります。
親コンポーネントへのデータ受け渡し
addItem(data)を呼び出すことで、親コンポーネントにデータを渡しています。これはJavaScriptの関数参照の仕組みによるものです。
親コンポーネントから渡されたaddItem関数は、propsを通じて子コンポーネントに「関数そのもの」が渡されています。そのため、子コンポーネントでaddItem(data)を実行すると、実際には親コンポーネントで定義された関数が実行され、親のstateを更新できます。
まとめ
React Hook Formを使用することで、以下のメリットが得られます。
コード量の削減: 従来の実装と比較して、状態管理やイベントハンドラのコードを大幅に削減できます。
宣言的なバリデーション: バリデーションルールをオブジェクトとして宣言的に記述できるため、可読性が向上します。
パフォーマンスの向上: 非制御コンポーネントの仕組みを活用することで、不要な再レンダリングを抑制できます。
型安全性: TypeScriptと組み合わせることで、フォームデータの型安全性を確保できます。
React Hook Formは、シンプルなフォームから複雑なフォームまで幅広く対応できる強力なライブラリです。基本的な使い方を理解することで、効率的にフォームを実装できるようになります。