react-hook-formのsetErrorではフォームの送信を止められない

2023/09/29に公開

Reactでフォームを扱うライブラリといえばreact-hook-form。
このライブラリは送信時に自動でエラーをチェックし、エラーがあれば送信をしないという便利な機能が備わっています。

そして一方でreact-hook-formには、手動でエラーを設定できるsetErrorという関数があります。
じゃあ 「これを使ってエラーを設定すればフォームの送信を防げる!」 と思ったら防げなかったので、その理由とreact-hook-formにおける基本的なバリデーション実装の方針をまとめます。

setErrorで送信を防げない理由

そもそもsetErrorは送信時のバリデーションとしてデザインされていないから

これが理由のようです。同様の事象で困っているissueがありました。
https://github.com/react-hook-form/react-hook-form/issues/226

こちらの回答によると、requiredminなどのバリデーションをしたいなら、そして送信を防ぎたいならsetErrorを使わずに素直にreact-hook-formのregisterオプションの方(あるいはzodなどとの組み合わせ)のバリデーションを使ってね、ということのようです。

公式より引用

This method will force set isValid formState to false, however, it's important to aware isValid will always be derived by the validation result from your input registration rules or schema result.

(DeepL翻訳)

このメソッドはisValid formStateを強制的にfalseに設定しますが、isValidは常に入力登録ルールやスキーマの結果からのバリデーション結果によって導かれることに注意しましょう。

setErrorでは、エラー状態にしてメッセージを表示することはできるが、送信時のバリデーションとしては力を発揮できないということですね。

いつsetErrorを使うのか

ではいつsetErrorを使えばいいのか、setErrorの存在意義は何なのかというと、主に非同期なバリデーションでその真の力を発揮します

例えば、次のような簡単な登録フォームを考えてみましょう。

  1. ユーザー名 (システム上でユニーク)
  2. パスワード

というフォームが並んでいたとして、ひとつめのユーザー名を入力し、パスワードフォームにフォーカスを移したとします。
このときユーザー名フォームのonBlurが発火しますが、ここにユーザー名がユニークかどうかをサーバーサイドでチェックする処理を挟みます。
そしてチェックの結果がエラーであればsetErrorでエラー状態にすることで、ユーザーはパスワードの入力中にユーザー名フォームのエラーに気付ける、という感じです。

プログラムのイメージは次のようになるでしょうか。

<input
  {...register('username')}
  onBlur={(e) => {
    // 非同期のバリデーション処理
    
    if (!data.isUnique) {
      setError('username', {
        type: 'manual',
        message: 'このユーザー名はすでに使われています'
      });
    }
  }}
/>

以上です。react-hook-formの機能のすみ分けやライブラリの意図を汲み取ることができた気がして勉強になりました。

Discussion