React Hook Form で他のフィールドの値を参照した validation を定義する方法とハマったところ
React Hook Form (以下、RHF)で他のフィールドの値を参照した validation を定義しようとしてほんのりハマったので、やり方をまとめてみました。
前提条件
react-hook-form@7.41.1
react@18.2.0
やりたいこと
以下のような最大値と最小値を入力してもらうフォームにおいて、最大値が最小値より大きくなるように validation を設定したい。
const App = () => {
const { register } = useForm();
return (
<form>
<input
type="number"
{...register('min', { valueAsNumber: true })}
/>
<input
type="number"
{...register('max', { valueAsNumber: true })}
/>
</form>
);
};
やり方
-
useForm
のregister
にvalidate
を定義してカスタムバリデーションを定義 - カスタムバリデーションの中で
useForm
のgetValues
を使って他のフィールドの値を取得
こんな感じ
const App = () => {
const { register, getValues } = useForm();
return (
<form>
<input
type="number"
{...register('min', {
valueAsNumber: true,
deps: ['max'],
})}
/>
<input
type="number"
{...register('max', {
valueAsNumber: true,
validate: (value) => {
if (isNaN(value)) { return true } // 未入力時
return value > (getValues('min') || 0)
},
)}
/>
</form>
);
};
ハマったところ
validate で型エラー
RHF の公式ドキュメント に書かれてた以下のサンプルコードにある validateNumber
を参考に第2引数に formValues
を渡した関数を定義した。
// object of callback functions
<input
{...register("test1", {
validate: {
positive: v => parseInt(v) > 0 || 'should be greater than 0',
lessThanTen: v => parseInt(v) < 10 || 'should be lower than 10',
validateNumber: (_: number, formValues: FormValues) {
return formValues.number1 + formValues.number2 === 3 || 'Check sum number';
},
// you can do asynchronous validation as well
checkUrl: async () => await fetch() || 'error message', // JS only: <p>error message</p> TS only support string
messages: v => !v && ['test', 'test2']
}
})}
/>
しかし、 validate
が型エラーになったので、 type Validate<TFieldValue>
の定義を確認することに。
ローカルの node_modules/ 内にある型定義ファイルを確認したところ、以下のように引数が1つで定義されていることが発覚して書き方を修正。
// v7.41.1 (search in node_modules/)
export type ValidateResult = Message | boolean | undefined;
export type Validate<TFieldValue> = (value: TFieldValue) => ValidateResult | Promise<ValidateResult>;
どうやら validate
の第2引数は v7.42.0
で実装されたようで、今回使っている v7.41.1
では未実装だったらしい。
※ちなみに node_modules/ を見る前に間違えて Github 上で v7.41.1
ではなく v7.42.1
の型定義ファイルをみてしまって混乱していた。錯覚いけないよく見るよろし。
公式ドキュメントのバージョンを誤解
RHF の公式ドキュメント では右上に「V5/V6」という表記があるので、「今表示しているのがV5/V6のドキュメントで、押すとV7に切り替わる」と思って読んでいた。
V5/V6 のドキュメントだと思って読んでいたが…
しかし、上記の validate
の第2引数が v7.42.0
で実装されたことから、「V5/V6」の部分を押すと過去のドキュメントに飛ぶようで、 「V5/V6」と表示されているページは最新の V7 のドキュメント らしい。
(わかりづらい!!)
初期状態で古い V7 のドキュメントが表示
「Version 7」の部分をクリックでバージョン選択
「Version 5/6」を選ぶとやっと V5/V6 のドキュメントを表示
上記の挙動については以下の Issue にて there is something to be improved here
と書かれており、改善予定の模様
まとめ
validate
を使うときは バージョンごとの差異と参照している公式ドキュメントのバージョンに注意しましょう!
Special Thanks: 黒曜さん
おまけ
valueAsNumber
を使うことで未入力時の値が NaN になるため、 validate に渡す関数内で isNaN
による判定をしている。
これについて POST や validation を考慮すると、以下の記事を参考に setValueAs
を定義して、未入力時は null になるように制御するのが良さそう。(本記事内ではサンプルコードが複雑になるのを避けるために valueAsNumber
を使ったパターンを紹介している。)
Discussion
superRefineで相関チェックをやってみました。
/min-max
ページがデモになります。簡単ですが、以上です。