3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React Hook Formで扱うグローバルエラー

Posted at

はじめに

Reactでフォームを扱う時どのように扱っていますか?ReactのAPIを用いたり、recoilなどの状態管理ライブラリを用いるなど様々な方法があると思います。その中でも私はReact Hook Formを用いて管理することが多いです。
この記事ではそんなReact Hook Formのバージョン7.43.0で追加されたグローバルエラーについて解説していきます。バージョン7.43.0ではある条件で型に関するバグがあるのでバージョン7.43.1以降を利用するようにしましょう(後述)。
この記事はこちらのissueによって提案された機能の解説となっています。

グローバルエラー

グローバルエラーは特定のフォームフィールドに紐づいていないエラーを指します。サーバーエラーやカスタムエラーなどに該当します。

これまで

グローバルエラーは今回正式にサポートされましたが、これまでも扱うことが出来ました。

export const SampleForm = (): JSX.Element => {
  const { handleSubmit, clearErrors, setError } = useForm();
  const onSubmit = () => {
    clearErrors();
    try {
      fetch(...);
    } catch(e: FetchError) {
      setError('server', { type: 'serverError', message: e.message });
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="text" name="hello" />
      <button>送信</button>
    </form>
  );
};

このコードは問題なく動作しますが一点DX上のデメリットがあります。
通常のエラーは再送信する時に自動的に一度エラーをリセットしますが、グローバルエラーはフォームフィールドに紐づいていないので再送信前にclearErrorsでエラーを手動で削除する必要があると言う点です。このような実装はライブラリの仕様を知らないとバグに繋がるなど保守の面でも不利な性能を持つので積極的に使いたい手法ではないと考えています。
さらに、以下のようにuseFormにより強い型制約を渡した場合にグローバルエラーをセットすると型エラーが出てしまう問題があります。

export const SampleForm = (): JSX.Element => {
  const {
    handleSubmit,
    clearErrors,
    setError
  } = useForm<{ hello: string }>();

  const onSubmit = () => {
    clearErrors();
    try {
      fetch(...);
    } catch(e: FetchError) {
      // ここでエラーが出ます(型 '"server"' の引数を型 '"hello"' のパラメーターに割り当てることはできません。)
      setError('server', { type: 'serverError', message: e.message });
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="text" name="hello" />
      <button>送信</button>
    </form>
  );
};

useFormにフォームフィールドの型情報{ hello: string; }を渡しているので、setErrorに始まりあらゆる関数のnameを要求する引数は'hello'だけになります。つまりグローバルエラーを扱いたいときはダミーの型情報も合わせて渡すか、緩い条件で扱うことが求められます。

7.43以降

これを解決したのが今回追加された機能です。この機能はReact Hook Formで扱えるエラーのnameにフォームフィールドの他に'root'`root.${string}`を渡すことを可能にするというものです。これによって先ほどのコードは以下のように書けるようになります。

export const SampleForm = (): JSX.Element => {
  const { handleSubmit, setError } = useForm<{ hello: string }>();

  const onSubmit = () => {
    try {
      fetch(...);
    } catch(e: FetchError) {
      setError('root.server', { type: 'serverError', message: e.message });
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="text" name="hello" />
      <button>送信</button>
    </form>
  );
};

型の制約によるエラーやセットしたエラーのリセットもする必要がないので、他のフォームフィールドと同じように扱うことができるようになりました。
バージョン7.43.0ではuseFormに型を渡した時は型エラーで設定できないことに注意してください(7.43.1で修正されました)。

おわりに

これまで私はグローバルなエラーを扱うときはこの記事で紹介したデメリットと天秤にかけてReact Hook Formとは別で用意した状態で管理していたので、この機能でフォームに関するエラーを一つの源からで扱えるようになって綺麗に書くことができるようになりとても嬉しいです。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?