React Hook Form で実現させるフォーム周りのアレコレ
https://gyazo.com/44da0748561c16d07946357fd34094c7
React Hook Form - Simple React forms validation
はじめに
Vue におけるフォームバリデーションの王道は VeeValidate (弊社内)ですが、React におけるフォームバリデーションにはどのライブラリを使えばよいでしょうか?
ここでは、 React Hooks 向けに設計されたフォーム関連のライブラリ React Hook Form を推しつつ基本的な使い方を紹介します。
⭐️ いくつかのサンプルコードを用意しました。React Hook Form PLAYGROUND - StackBlitz
React Hook Form とは?
Home | React Hook Form - Simple React forms validation
React Hook Form は非制御なコンポーネントに対して値のバインディングやバリデーションを組み込めるライブラリです。
競合ライブラリ
React のフォーム関連の主要ライブラリとの比較が公式 FAQs に記載されています。
FAQs - React Hook Form, Formik or Redux Form?
ここでの推しポイントとしては「非制御コンポーネント」「サイズが小さい」「学中曲線が比較的低い」「バリデーションルールを各種スキーマライブラリと連携させやすい」あたりです。
tamuraryoya.icon 個人的にも React Hooks 時代に出現したライブラリのためモダンな環境に馴染みやすいため学びやすい&使いやすいと思う
tamuraryoya.icon そもそも React でフォームだったりバリデーションだったりをあまり実装したことないので、このライブラリのがいいよ!とかあれば教えてほしい
制御コンポーネント と 非制御コンポーネント
制御コンポーネントは ステートと要素の状態を同期させたコンポーネント を指します。たとえば、以下の <input /> は制御コンポーネントです。
code:tsx
const ControlledInputForm = () => {
const value, setValue = useState('Hello! You.');
return (
<form onSubmit={() => console.log(value)}>
<input value={value} onInput={(e) => setValue(e.target.value)} />
</form>
);
}
制御コンポーネントの問題点は React による制御が入るまで(JSの読み込みが完了するまで)に操作された状態が引き継げない点です。
VeeValidate などは入力欄などを制御コンポーネントとして処理するため、JS読込中に入力した内容は VeeValidate が初期化された際に初期値にリセットされてしまいます。
Forms – React
それに対して、非制御コンポーネントは ステートと要素の状態を分離させたコンポーネント を指します。たとえば、以下の <input /> は非制御コンポーネントです。
code:tsx
const UncontrolledInput = () => {
const inputRef = useRef();
return (
<form onSubmit={() => console.log(inputRef.current.value)}>
<input ref={inputRef} defaultValue="Hello! You." />
</form>
);
}
Uncontrolled Components – React
非制御コンポーネントはステートとの同期を取らないため、JSが読み込まれるまでに更新された入力欄の状態がリセットされることはありません。ただ、実装上は React 上の状態と DOM 上の状態が常に一致するわけではなくなるため、実装が煩雑になります。
React のドキュメントでは 制御コンポーネント での実装が推奨されていますが、ユーザーフレンドリーなのは 非制御コンポーネント での実装です。
基本的な使い方
useForm から返却される各種メソッド・ステートからデータバインディングやバリデーションを登録できます。
大体の使い方は公式ドキュメントがとても充実しています。
Get Started | React Hook Form - Simple React forms validation
スキーマでのバリデーションルール定義
バックエンドのリクエストバリデーションのようなスキーマをフォームバリデーションに使用することもできます。
公式ドキュメントでは yup というライブラリが使用されていますが、 Zod や Joi といった主要なスキーマライブラリを連携するリゾルバが提供されています。
Get Started - スキーマ検証
実現させたいアレコレ
リアクティブなフォーム
useForm - watch | React Hook Form - Simple React forms validation
入力中の値をリアルタイムに表示したり、useEffect の依存に含めたりしたい場合は watch を用いて次のように実装します。
code:tsx
const { register, watch } = useForm();
const currentName = watch('name');
useEffect(() => { }, currentName);
return (
<>
<input {...register('name')} />
<p>{currentName}</p>
</>
)
https://stackblitz.com/edit/rhfpg?file=src%2Fexamples%2FReactiveForm.tsx
型安全なスキーマバリデーション
yup を使ったスキーマベースのバリデーションで TypeScript の型推論を行き渡らせるためには次のような型定義を追加します。
code:tsx
const schema = yup.object({
name: yup.string().required(),
});
const {
register,
formState: { errors },
} = useForm<yup.InferType<typeof schema>>({ resolver: yupResolver(schema) });
return (
<>
<input {...register('name')} /> {/* 'name' 以外は型エラー */}
<p>{errors.name?.message}</p> {/* errros.name 以外は型エラー */}
</>
)
https://stackblitz.com/edit/rhfpg?file=src%2Fexamples%2FReactiveForm.tsx
増減するフィールド
useFieldArray | React Hook Form - Simple React forms validation
便利な使い方
useForm() の引数
useForm | React Hook Form - Simple React forms validation
mode
onSubmit / onBlur / onChange / onTouched / all から選択(デフォルトは onSubmit )
reValidateMode
一度バリデーションが実施されたあとのバリデーションの実施タイミング
onChange / onBlur / onSubmit から選択(デフォルトは onChange )
defaultValues
各フィールドのデフォルト値を指定できる
yup の場合、スキーマでもそれぞれのデフォルト値を指定できる
criteriaMode
収集するエラーを最初の1件(デフォルト)か、全件か指定できる
shouldFocusError
エラーが発生したフィールドへフォーカスするかを選択(デフォルトは true )
DevTools
API Documentation | React Hook Form - Simple React forms validation
フォームの内部状態やエラーなどを確認できる DevTools が提供されている。
導入も各フォームで
Advanced
Advanced Usage | React Hook Form - Simple React forms validation
アクセシビリティ対応、ページ分割されたフォーム、react-hook-form を内包したコンポーネントの作成、バーチャルリストでのバリデーション、react-hook-form を含むフォームのテスト方法などを実際のコードを示して説明してくれています。
調べきれていないところ
スキーマベースのバリデーションルール管理
慣れの問題だと思うが、スキーマベースでバリデーションルールを管理する方法がピンときていない。
yup はAPIのバリデーションなどでよく使われているライブラリなので、同じようなベストプラクティスに乗っかれば問題なく運用できる気はする。
終わりに
React Hook Form は仕組みが React Hooks に似た設計思想で作られているため、直感的に機能を理解できて使い始めやすいライブラリだと感じました。
また、スキーマベースでバリデーションルールを定義できるため API の設計などとの相性の良さや、ライブラリ独自のルールを学ぶ必要がないのも良いなと思います。
ただ、バリデーションできるコンポーネントの実装方法や、他のライブラリと比べた知見の少なさがややネックかなとは思いますが、公式ドキュメントがかなり充実しているので特殊なことをしない限りは困ることはなさそうです。
tamuraryoya.icon バリデーションライブラリとしてではなく、フォームのデータバインディングとしてだけでも十分に使いやすいライブラリだったので今後は onInput={(e) => setValue(e.target.value)} は書かずに生きていこうと思います。
tamuraryoya.icon yup の number().required().positive().integer() が壊れている(米谷さん・宮崎さん)
tamuraryoya.icon type="file" の実装がつらめ(小林さん)
おまけ
本当は ある案件の前半のフロント開発を振り返る の後半戦を紹介しようと思いましたが、あんまり明確に思い出せずに内容が中途半端になってしまったのでボツりました。印象的だった経験はちゃっちゃと文書化しよう(自戒)。
ある案件の後半のフロント開発を振り返る
#React #react-hook-form #勉強会
Created by tamuraryoya