🎃
ReactHookForm setErrorでsubmitを止めたい
環境
React Hook Form v17
useForm の setError
エラーを手動で設定できる。
setError('registerInput', { type: 'custom', message: 'custom message' })
何がしたかったのか
emailの入力値がすでにDBに登録されている場合に、他のバリデーションと同様にエラー表示し、submitさせないようにしたい。
- 入力フォーム(input要素)にonBlurを設定
- 入力値をwatchで取得して存在確認用のAPIを呼ぶ
- APIの結果によってエラーを設定する(ここでsetErrorを用いる)
躓いた点
- setErrorを設定するだけではsubmitが走ってしまう。
似たようなことで困っているgithubのissue。
そもそもバリデーションを設定するものではない。本来の使いどころは以下のような場合の時。
Can be useful in the handleSubmit method when you want to give error feedback to a user after async validation. (ex: API returns validation errors)
解決方法
バリデーションに'email'の派生に当たる'email_exists'を作成して追加する。
yupを使用しているので以下のような感じの定義となる。
email_exists: yup
.bool()
.oneOf([false], 'すでに登録されている値です')
.default(false),
処理の流れ
- emailの入力。フォーカスが外れたときに onBlurEmail() の呼び出し(以下のサンプル)
- apiの実行(既存な値があるという結果返ってくる)
- 'email_exists' に対して、setValue & setError
- setError があると入力後すぐにエラーを画面に表示できる
- 'email_exists' でバリデーションが効いて onSubmit に至らない
const onBlurEmail = () => {
//初期化
clearErrors('email_exists')
setValue('email_exists', false)
//api呼び出しは省略
setValue('email_exists', true)
setError('email_exists', {
type: 'custom',
message: 'すでに登録されている値です',
})
}
失敗した方法
shouldFocusを設定する方法。
- api呼び出しの結果、エラーを表示
- shouldFocusが効いているので、emailのinput要素にフォーカスが当たる
- そのままのエラーを残した状態でフォーカスを外すとonBlurが走る。再度apiが呼び出される
- 結果は変わらないのでエラー表示。フォーカスが戻ってくる(無限ループ)
*正しくない値が入力され続ける限りフォーカスされ続ける。navのリンクなど、全く関係のない個所を押そうとしても、フォーカスが外れることで再度実行されるため、無限ループに陥る。
setError('registerInput', { type: 'custom', message: 'custom message' }, { shouldFocus: true })
import { useForm } from "react-hook-form";
const App = () => {
const { register, setError, formState: { errors } } = useForm();
const handleBlur = () => {
//api呼び出しは省略
setError('email', { type: 'custom', message: 'custom message' }, { shouldFocus: true });
}
const onSubmit = () => {(略)}
return (
<form>
<input {...register("email")} onBlur={handleBlur} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit" >submit</button>
</form>
);
};
Discussion