React Hook Formの基本まとめ その1(React Hook Formのみ使用するパターン)
こんにちは! 今回はReact Hook Formについてまとめます。
React Hook Formとは
- Reactのフォームライブラリ
- 簡潔な記述でコード量を減らしながら、レンダリングを抑えることが出来るのでパフォーマンスも向上する
- 以下RHFと略す
RHFのみ使用する場合
- まずは一番シンプルなRHFのみ使用したパターンから説明
サンプルコード
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)}> <input {...register('name', { required: 'このフィールドは必須です' })} /> {errors.name && <p>{ errors.name.message }</p>} <button type="submit">送信</button> </form> ) }
解説
useForm
- RHFを使うためのフック
- ここから必要なメソッドや状態を取得する
register
- RHFでフォームを管理するためのメソッドや状態が含まれているオブジェクト
- registerの中身をinputタグなどにスプレッド構文ですべて付与することで、付与されたinputタグがRHFの管理下に置かれる
handleSubmit
- RHFで管理されたフォームをsubmitする時に使用する
- フォームの値を集約してonSubmitにオブジェクトとして渡す
formState
- フォームの状態を保持するオブジェクト
- formState: { errors } とすることで、よく使用するerrorsにアクセスしやすいように、
errors単体で変数として取り出している
ポイント
...register について
- RHFのよくわからないポイントだった ...registerについて苦手を解消する
- まずこれが何をしているかというと、対象のinputをRHFの管理下に置いているということ
- どういうことかというと対象のinputの入力内容をRHFが検知して状態として保持することができるようになる
- 仕組みとしては、registerの第一引数である name などのラベルに紐づいた
onChangeやonBlur、refを生成し、それをinputに登録しているイメージ - registerとは、引数にラベルやバリデーションを受け取り
onChangeやonBlur、refを含むオブジェクトを返すメソッド - イメージとしては次のようなオブジェクトを返す
{ name: "name" onChange: () => {}, onBlur: () => {}, ref: () => {} }
- そのため、registerが返すこのオブジェクトをスプレッド構文を使って展開してinputに登録することで、対象のinputに
onChangeやonBlur、refが登録される仕組み
inputタグのname属性について
- 基本だが、inputタグのname属性はフォームデータの識別子として使われる
- 送信するときに各フィールドの名前(キー)として使われる
- name属性はユニークであるべきか
- ユニークでなくてもよいが、意味を持たせる必要がある
- チェックボックスなどで複数のボックスに同じ名前を持たせた場合、同じキー名で値が配列になって渡される
- RHFではname属性がキーになるので、同じフォーム内ではユニークにするべき
- RHFでnameを使って複数の入力をグループ化する方法
<form onSubmit={handleSubmit(onSubmit)}> <input {...register("user.email")} placeholder="Email" /> <input {...register("user.username")} placeholder="Username" /> </form>
- 上記のデータは次のように送られる
<form onSubmit={handleSubmit(onSubmit)}> <input {...register("user.email")} placeholder="Email" /> <input {...register("user.username")} placeholder="Username" /> </form>
RHFがデフォルトでバリデーションを行うタイミング
onSubmit時onBlur時
formState: { errors } について
- ここの記述に苦手意識があったが、これはネストされた分割構文
- useForm() の戻り値は次のようなイメージ
{ register: ...., handleSubmit: ..., formState: { errors: { name: ..., email: ...., } } }
- この中から、registerやhandleSubmitを抽出するために、
const { register, handleSubmit } = useForm()と記述している const { register, handleSubmit, formState } = useForm()と書いても良いが、その場合は、errorsにアクセスしたいときは、formState.errorsと書かないといけない- errorsはよく使うので、
const { register, handleSubmit, formState: { errors }} = useForm()としておけば、直接 errors にアクセスできる - またこの状態でも formStateにアクセスすることは可能
RHFでhandleSubmitを使う理由
- event.preventDefault() を行ってくれるので、ユーザーが記述する必要がない
- フォームの各データを収集してくれる
- バリデーションを実行し、エラーがあれば
formState.errorsに格納する - (エラーがなければ)収集したデータをオブジェクトとしてonSubmitに渡してくれる
もしhandleSubmitを使わなければ
- event.prevent.Default()を記述しないといけない
- onSubmitには
FormEventオブジェクトが渡される - 自分で
event.targetから値を取得しないといけない
registerの第二引数に指定するオプション
- RHFの苦手なポイントとして、第二引数が複雑というイメージがある
required
<input {...register('name', { required: true })} /><input {...register('name', { required: '名前は必須です' })} />
min
<input {…register('age', { min: 5 })} ><input {…register('age', { min: {value: 5, message: '年齢は5歳以上である必要があります' } })} >
max
<input { ...register( 'age', { max: 5 })} /><input { ...register( 'age', { max: { value: 5, message: '年齢は5歳以下である必要があります' } })} />
minLength
<input {…register('name', { minLength: 5 })} /><input {…register('name', { minLength: { value: 5, message: '5文字以上で入力してください' } })} />
maxLength
<input {…register('name', { maxLength: 5 })} /><input {…register('name', { maxLength: { value: 5, message: 5文字以下で入力してください } })} />
pattern
<input {…register('name', { pattern: { value: /[...]/ , message: '正しい形式で入力してください' } })} />
validate
<input {…register('name', { validate: (value)=> value === 'admin' ? 'error' : true })} />
<input {...register('name', { validate: { notAdmin: (value) => value !== 'admin' || 'adminは使用できません', minLength: (value) => value.length >=5 || '5文字以上で入力してください' } })} />
disabled
<input {…register('name', { disabled: true })} />
valueAsNumber
<input {…register('name', { valueAsNumber: true })} />
valueAsDate
<input {…register('name', { valueAsDate: true })} />
ポイント
- 大体このようにまとめてみると、第二引数は
{ パラメータ: boolean }または{ パラメータ: 値 }の基本形があり、メッセージを付けたい時には{ パラメータ: { value: 値, message: メッセージ } }になるような傾向がある(例外あり)
おわりに
長くなってきたので記事を分けようと思います。今回はReact Hook Formだけを使用したパターンでした。次はzodを組み合わせたバリデーションについてまとめます。