React Hook Form使ってみる
React Hook Formとは?
公式
React Hookを利用した柔軟かつ拡張可能な使いやすいフォームバリデーションライブラリ。
導入するメリット
- バリデーションが比較的簡単に実装できる
- レンダリングが抑えられる⇒パフォーマンス向上
- 依存性が少ない
- 軽量なパッケージ
- 公式のドキュメントが豊富&日本語
などがあげられるようです。
環境構築
- Next.js
- TypeScript
- react-hook-form "^7.14.2"
- MaterialUI(別記事予定)
今回はreact-hook-formのみで簡単なフォームを作成します。
導入
yarn add react-hook-form
ですぐにインストールされます。
早速やってく
今回は公式のコードを参考に解説を交えながらやっていきます。
フィールドに登録する
import { useForm } from 'react-hook-form';
enum GenderEnum {
female = 'female',
male = 'male',
other = 'other',
}
interface IFormInput {
firstName: String;
gender: GenderEnum;
}
const App = () => {
const { register, handleSubmit } = useForm();
const onSubmit = (data: IFormInput) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor=''>First Name</label>
<input type='text' {...register('firstName')} />
<label htmlFor=''>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>
);
};
export default App;
型定義については省略します。
const { register, handleSubmit } = useForm();
useForm()内からregisterとhandleSubmitを呼び出します
- registerは登録するために必要なもの。(日本語でも登録という意味です。初知り)
<input type='text' {...register('firstName')} />
このように記述することで'firstName'というキーでフィールドに登録されます。
こちらはv7から変更された記述法になります。
const onSubmit = (data: IFormInput) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
handleSubmitは、onSubmitの実行前に、フォームバリデーション(後述)を実行します。 onSubmitは、引数としてフォームデータを受け取ります。
preventDefaultを実行しなくても挙動をキャンセルしてくれています。
これで簡単なフォームは完成です。
バリデーションを適用する
- required:Boolean trueの場合フォームを送信する前に入力値が必須
- min:number 最小数
- max:number 最大数
- minLength:number 最小文字数
- maxLength:number 最大文字数
- pattern:正規表現で様々なバリデーションができる
- validate:コールバック関数でいろいろできる
import { useForm } from 'react-hook-form';
enum GenderEnum {
female = 'female',
male = 'male',
other = 'other',
}
interface IFormInput {
firstName: string;
lastName: string;
age: number;
gender: GenderEnum;
}
const App = () => {
const { register, handleSubmit } = useForm();
console.log(register.name);
const onSubmit = (data: IFormInput) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor='firstName'>First Name</label>
<input
id='firstName'
type='text'
// 文字列が1~20以外だった場合エラーがでる
{...register('firstName', { required: true, maxLength: 20 })}
/>
<label htmlFor='lastName'>Last Name</label>
<input
type='text'
id='lastName'
// アルファベットならよし
{...register('lastName', { pattern: /^[A-Za-z]+$/i })}
/>
<label htmlFor='age'>Age</label>
<input
id='age'
type='number'
// 18以上99以下ならよし
{...register('age', { min: 18, max: 99 })}
/>
<label htmlFor='gender'>Gender Selection</label>
<select id='gender'
// 入力しないとエラーが出るが、入力されていないことがないはずなので意味なし
{...register('gender', { required: true })}>
<option value='female'>female</option>
<option value='male'>male</option>
<option value='other'>other</option>
</select>
<input type='submit' />
</form>
);
};
export default App;
コード中で解説してるようなバリデーションが簡単に実装できます。
エラーを処理する
またuseForm()からerrosオブジェクトを取り出し、エラーの場合の処理を実装することもできます。
const {
register,
formState: { errors },
handleSubmit,
} = useForm();
今回は簡単に、エラーが出た場合、エラー文を表示するようにしてみます。
{/* フィールドに登録されているfirstNameでエラーが出た場合文字が出る */} {errors.firstName && 'FirstName is required'}
このように記述することで
erros.firstNameがtrue つまりエラーが発生している時に簡単にエラー文が表示できます。
resetを実装する
フォームが送信された時にフォームを空にして欲しいので、その処理も実装してみます。
useStateを使ってないので、どうするんだろう?と思って色々やりつつ公式を見てたらちゃんといいのがありました(笑)
const {
register,
formState: { errors },
handleSubmit,
reset,
} = useForm();
useFormからresetを取り出して、
const onSubmit = (data: IFormInput) => {
console.log(data);
reset();
};
onSubmit関数が実行された時にreset関数を実行するだけで、登録されているフィールドがresetされます。
defaultValueを設定したり、特定のフィールドのみをリセットしたりできますが今回は省略します。
以上で最低限の機能を実装できたのではないかと思います。
公式のドキュメントを見る限り、他にもたくさんの機能が盛りだくさんです。
私の方では別記事でMaterialUIと組み合わせたフォームを作成していこうかと思います。
ありがとうございました。
最終的なコード
import { useForm } from 'react-hook-form';
enum GenderEnum {
female = 'female',
male = 'male',
other = 'other',
}
interface IFormInput {
firstName: string;
lastName: string;
age: number;
gender: GenderEnum;
}
const App = () => {
const {
register,
formState: { errors },
handleSubmit,
reset,
} = useForm();
const onSubmit = (data: IFormInput) => {
console.log(data);
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor='firstName'>First Name</label>
<input
id='firstName'
type='text'
// 文字列が1~20以外だった場合エラーがでる
{...register('firstName', { required: true, maxLength: 20 })}
/>
{/* フィールドに登録されているfirstNameでエラーが出た場合文字が出る */}
{errors.firstName && 'FirstName is required'}
</div>
<div>
<label htmlFor='lastName'>Last Name</label>
<input
type='text'
id='lastName'
// アルファベットならよし
{...register('lastName', { required: true, pattern: /^[A-Za-z]+$/i })}
/>
{errors.lastName && 'LastName is required'}
</div>
<div>
<label htmlFor='age'>Age</label>
<input
id='age'
type='number'
// 18以上99以下ならよし
{...register('age', { required: true, min: 18, max: 99 })}
/>
{errors.age && 'Age is required'}
</div>
<div>
<label htmlFor='gender'>Gender Selection</label>
<select
id='gender'
// 入力しないとエラーが出るが、入力されていないことがないはずなので意味なし
{...register('gender', { required: true })}
>
<option value='female'>female</option>
<option value='male'>male</option>
<option value='other'>other</option>
</select>
</div>
<input type='submit' />
</form>
);
};
export default App;
Discussion