💡
React Form HookとYupを使って、validationとstate管理を楽にする
はじめに
SREホールディングス株式会社にて、ソフトウェアエンジニアをやっております、叶です。
今回はフロントエンド(React)のライブラリreact-form-hook
yup
組み合わせて使うことによって、画面のvalidationとstate管理を楽にする方法を紹介いたします。
対象読者
- Reactを用いて、フォーム開発を楽にしたい方
使用したライブラリ(React)
- react-form-hook(7.49.3): フォームを管理するライブラリです。
- yup(1.2.0): Jason Quense氏が開発している、バリデーションのためのライブラリです。
Yup
Yupとは
フォームの入力値を解析してバリデーションを行うために、JavaScriptでスキーマ(データ構造)を定義するためのライブラリです。
Yupを用いて、やったこと
- フォームのスキーマを定義し、スキーマからフォームタイプに変換する
- 項目ごとにvalidationを定義する
- ラベル名を定義する
- メッセージを定義する
React Form Hook
React Form Hookとは
Reactでフォームを簡単に扱うことのできるライブラリです。
※フォームのvalidationの定義もできますが、今回はReact Form Hookの代わりにYupを使います。
React Form Hookを用いて、やったこと
- フォームの項目のstateを
React Form Hook
で管理できる(いちいちuseStateを使わなくても済む) -
isDirty(変更ありなし)
やisValid(入力値が正しいか否か)
などを使って、フォーム全体または個別のフィールドのstateを確認することができる
ハンズオン
Yupでメッセージの定義
Yupのデフォルトエラーメッセージはエラーになった項目の英語名で表示するので、開発者じゃないとわからないですが、定義したラベルを使用して、メッセージを設定し直すことにより、共通でわかりやすいメッセージを定義することができます。
// メッセージパラメータのタイプを定義
interface MessageParams {
path: string;
value: any;
originalValue: any;
label: string; // スキーマに定義したlabelの値がこちらに設定される
type: string;
}
const labelText = (prm: MessageParams) => {
return prm.label ? `${prm.label}は` : "";
};
const jpConfig = {
mixed: {
default: (prm: MessageParams) => `${labelText(prm)}無効です。`, // デフォルトエラーメッセージ
required: (prm: MessageParams) => `${labelText(prm)}必須です。`, // 必須エラーメッセージ
notType: (prm: MessageParams) => {
if (prm.type === "number") {
// 数字タイプエラーメッセージ
return `${labelText(prm)}数字でご入力ください。`;
} else {
return `${labelText(prm)}無効です。`;
}
}
}
};
yup.setLocale(jpConfig);
↓メッセージ定義で参考した記事↓
スキーマ及びラベルを定義
const userSchema = yup.object({
userName: yup.string().label("ユーザー").required(),
email: yup.string().label("メールアドレス").required(),
age: yup.number().label("年齢").required()
});
スキーマからタイプに変換
type UserType = yup.InferType<typeof userSchema>;
React Form Hookを定義
- 返却した項目について
- reset: デフォルト値をリセットか、フォームの項目をデフォルトにリセットする
- handleSubmit: サブミットメソッドを登録する
- register: フォーム項目を登録する
- getValues: フォームの値を取得する
- formState: フォームのステート(
バリデーション結果
変更有無
デフォルト値
エラーメッセージ
など)を取得する
- mode: バリデーションを実行するタイミングをフォーカスアウトにする
- yupResolverライブラリを使って、
react-form-hook
のresolverに設定することで、定義したvalidationが動作する
const {
reset,
handleSubmit,
register,
getValues,
formState: { isValid, isDirty, defaultValues, errors }
} = useForm<UserType>({
mode: "onBlur",
resolver: yupResolver<UserType>(userSchema)
});
イベントを定義
- サブミット
-
reset
に値を入れると、フォームのデフォルト値が再設定される
-
const submit = () => {
reset(getValues());
};
<!-- 省略 -->
<Button
onClick={handleSubmit(submit)}
disabled={!(isValid && isDirty)}
>登録</Button>
- リセット
-
reset
を実行すると、フォームの項目を設定したデフォルト値にリセットされる
-
const resetUser = () => {
reset();
};
<!-- 省略 -->
<Button onClick={resetUser}>リセット</Button>
フォームを作成
- yupで定義したフィールドを利用して、フォームを作成する
{Object.entries(userSchema.fields).map(([key, field]) => (
<Stack
direction="column"
gap={2}
alignItems="left"
justifyContent="flex-start"
width="300px"
>
<!-- 表示するラベルはスキーマに定義したラベルを利用する -->
<Typography>{field.spec.label}</Typography>
<TextField
{...register(key)}
error={!!errors[key]}
helperText={(errors && errors[key]?.message) ?? ""}
fullWidth
/>
</Stack>
))}
Codepenでお試し
まとめ
今まで、フォームの項目が多い際に、悩んでいた問題がReact Form Hook
とYup
を使って、解決できて、楽になりましたね。
- useStateは面倒
-
React Form Hook
で管理にした
-
- 必須チェックのメッセージは項目ごとに定義しないといけない
-
Yup
で共通的なエラーのメッセージを自定義することで、いちいち書かなくても済む
-
- 項目定義を探すのは面倒
- 項目属性(項目日本語名を含む)をYupで統一なところに定義することによって、一目瞭然になった
Discussion