React yup の cast エラー を いい感じで処理する
React + Typescript + react-hook-form + yup で環境構築しています。
数値項目を入力するフィールドに、数値以外を入力したときに yup で表示される castエラーや、日付項目を入力するフィールドに、日付以外を入力したときに yup で表示される castエラーを、いい感じで処理する tips です。
サンプルは数値入力項目で書いていますが、日付項目などでも同じです。
yup の導入の仕方などについてはコチラ
【2022年】 React Hook FormでValidationライブラリはどれにするか?
標準の動作
たとえば数値項目に数値以外のものを入力すると yup は以下のように
「must be a number
type, but the final value was: NaN
(cast from the value ""
).」
と表示します。
export const createSchema = yup.object({
numberField: yup.number().label('数値入力項目')
})
数値以外の入力は 0 として処理する
数値以外の値を入力された場合はエラーにせず 0 として処理するには、transform
メソッドを使用して 0 に変換します。
export const createSchema = yup.object({
numberField: yup.number().label('数値入力項目')
.transform((value, originalvalue) => (originalvalue === '' ? 0 : value))
})
transform
メソッドで 0 に変換した後、最小値や最大値の検証メソッドを繋げます。
export const createSchema = yup.object({
numberField: yup.number().label('数値入力項目')
.transform((value, originalvalue) => (originalvalue === '' ? 0 : value))
.min(-999).max(999)
})
数値以外の入力はエラーにして日本語のメッセージにする
数値以外の値を入力された場合、日本語のエラーを表示するには typeError
メソッドで日本語メッセージを指定します。
export const createSchema = yup.object({
numberField: yup.number().label('数値入力項目')
.typeError('数値を入力してください。')
.min(-999).max(999)
})
yup の configファイル で日本語化する
以前、こちらの記事で yup のエラーメッセージを日本語化しました。
React 今更だけど改めて Validation ライブラリ yup の日本語化
yup 日本語化の為の config ファイルを再掲します。
typeError は mixedプロパティの notType プロパティにあたります。
import * as yup from 'yup'
import { MessageParams } from "yup/lib/types"
const labelText = (prm: MessageParams) => {
return prm.label ? `${prm.label}は`: ''
}
const jpConfig = {
mixed: {
default: (prm: MessageParams) => `${labelText(prm)}無効です`,
required: (prm: MessageParams) => `${labelText(prm)}必須の入力項目です`,
oneOf: (prm: MessageParams & {values: any}) => `${labelText(prm)}次の値のいずれかでなければなりません:${prm.values}`,
notOneOf: (prm: MessageParams & {values: any}) => `${labelText(prm)}次の値のいずれかであってはなりません:${prm.values}`,
+ notType: `形式が違います`,
defined: ``
},
string: {
length: (prm: MessageParams & {length: number}) => `${labelText(prm)}${prm.length}文字でなければなりません`,
min: (prm: MessageParams & {min: number}) => `${labelText(prm)}少なくとも${prm.min}文字でなければなりません`,
max: (prm: MessageParams & {max: number}) => `${labelText(prm)}最大${prm.max}文字でなければなりません`,
matches: (prm: MessageParams & {regex: RegExp}) => `${labelText(prm)}次の形式と一致する必要があります: "${prm.regex}"`,
email: (prm: MessageParams & {regex: RegExp}) => `${labelText(prm)}メールアドレス形式である必要があります`,
url: (prm: MessageParams & {regex: RegExp}) => `${labelText(prm)}有効なURLでなければなりません`,
uuid: (prm: MessageParams & {regex: RegExp}) => `${labelText(prm)}有効なUUIDでなければなりません`,
trim: (prm: MessageParams) => `${labelText(prm)}前後にスペースを入れてはいけません`,
lowercase: (prm: MessageParams) => `${labelText(prm)}小文字でなければなりません`,
uppercase: (prm: MessageParams) => `${labelText(prm)}大文字でなければなりません`,
},
number: {
min: (prm: MessageParams & {min: number}) => `${labelText(prm)}${prm.min}以上である必要があります`,
max: (prm: MessageParams & {max: number}) => `${labelText(prm)}${prm.max}以下でなければなりません`,
lessThan: (prm: MessageParams & {less: number}) => `${labelText(prm)}${prm.less}より小さくなければなりません`,
moreThan: (prm: MessageParams & {more: number}) => `${labelText(prm)}${prm.more}より大きくなければなりません`,
positive: (prm: MessageParams & {more: number}) => `${labelText(prm)}正の数でなければなりません`,
negative: (prm: MessageParams & {less: number}) => `${labelText(prm)}負の数でなければなりません`,
integer: (prm: MessageParams) => `${labelText(prm)}整数でなければなりません`,
},
date: {
min: (prm: MessageParams & {min: Date | string}) => `${labelText(prm)}${prm.min}より後でなければなりません`,
max: (prm: MessageParams & {max: Date | string}) => `${labelText(prm)}${prm.max}より前でなければなりません`,
},
boolean: {
isValue: (prm: MessageParams) => `${labelText(prm)}値が必要です`,
},
object: {
noUnknown: (prm: MessageParams) => `${labelText(prm)}オブジェクトシェイプで指定されていないキーを含めることはできません`,
},
array: {
length: (prm: MessageParams & {length: number}) => `${labelText(prm)}${prm.length}個が必要です`,
min: (prm: MessageParams & {min: number}) => `${labelText(prm)}${prm.min}個以上の項目が必要です`,
max: (prm: MessageParams & {max: number}) => `${labelText(prm)}${prm.max}個以下の項目が必要です`,
},
}
yup.setLocale(jpConfig)
export default yup
yup のスキーマ定義を変更して実行してみます。
export const createSchema = yup.object({
numberField: yup.number().label('数値入力項目')
.min(-999).max(999)
})
config に定義されているメッセージが表示されています。
config の内容を変更して、もうちょっといい感じにしてみます。
数値項目なら「xxxxxは数値を入力してください。」日付項目なら「xxxxxは日付を入力してください。」と表示するようにconfigファイルの内容を変更します。
mixed: {
default: (prm: MessageParams) => `${labelText(prm)}無効です`,
required: (prm: MessageParams) => `${labelText(prm)}必ず入力してください。`,
oneOf: (prm: MessageParams & {values: any}) => `${labelText(prm)}次の値のいずれかでなければなりません:${prm.values}`,
notOneOf: (prm: MessageParams & {values: any}) => `${labelText(prm)}次の値のいずれかであってはなりません:${prm.values}`,
- notType: `形式が違います`,
+ notType: (prm: MessageParams) => {
+ if (prm.type === 'number') {
+ return `${labelText(prm)}数値を入力してください。`
+ }
+ if (prm.type === 'date') {
+ return `${labelText(prm)}日付を入力してください。`
+ }
+ return `${labelText(prm)}正しい形式を入力してください。`
+ } ,
defined: ``
},
わかってしまえばなんてこともない処理なのですが typeError == mixed.notType に気付けなくて思わず時間喰ってしまった処理でした。
Discussion