反応フック形式によるフォーム検証


フォームは、任意のアプリケーションに存在するコア機能のいずれかです.この機能は、ユーザーデータ(入力)と対話し、貴重なデータやリソース(出力)に処理できます.
この機能も強力です、それは1つの最大の障害を持っています.はい、ユーザー入力を検証する方法です.そして、それが私がこの記事を作る理由です.
この記事では、パッケージを使用して反応アプリケーションで具体的にフォーム検証をどのように処理することができますかreact-hook-form .
それを始めましょう!

要件

  • 反応アプリケーション(新鮮または既存のアプリケーション)
  • NOTE:
    I will use my previous project called example-app. It has no features. Just a fresh React project installed using CRA.


    ステップ


    追加フックフックフォーム


    私は、内部でgitを使いますexample-app . そこで、パッケージを追加する前に新しいブランチを作成しますfeat-signin その後、その機能を終了するたびにブランチメインにその機能をマージします.
    # Create and checkout to branch feat-signin
    git checkout -b feat-signin
    
    今、パッケージを追加する時間です.
    yarn add react-hook-form
    

    2 .フォームの作成


    たぶん、あなたは私がどのようなフォームを構築したいかについての手がかりを持っています.はい、それは形の印です.私は実際にログインする前にユーザのメールとパスワードを検証します.
    私はスタイリングツールを使用しません.HTMLを簡単にしてフォーカスするには:.
    まず、プロジェクトの内部にsigninというページを追加したいです.
    # Here's my current project
    .
    ├── package.json
    ├── public
    │   ├── favicon.ico
    │   ├── index.html
    │   ├── logo192.png
    │   ├── logo512.png
    │   ├── manifest.json
    │   └── robots.txt
    ├── README.md
    ├── src
    │   ├── App.css
    │   ├── App.js
    │   ├── App.test.js
    │   ├── index.css
    │   ├── index.js
    │   ├── logo.svg
    │   ├── pages
    │   │   └── Signin.js
    │   ├── reportWebVitals.js
    │   └── setupTests.js
    └── yarn.lock
    
    フォーム内の記号を作成し、内部にインポートしますApp.js .
    // pages/Signin.js
    function Signin() {
      return (
        <div>
          <form>
            <div>
              <label htmlFor="email">Email</label>
              <input type="email" id="email" />
            </div>
            <div>
              <label htmlFor="password">Password</label>
              <input type="password" id="password" />
            </div>
            <div>
              <button>Signin</button>
            </div>
          </form>
        </div>
      );
    }
    
    export default Signin;
    
    // App.js
    import Signin from './pages/Signin';
    
    function App() {
      return <Signin />;
    }
    
    export default App;
    

    3 .フォームをフック形式で統合する


    フォーム内のすべてのユーザー入力を収集できるように、前のフォームを統合しましょう.
    // pages/Signin.js
    import { useForm } from 'react-hook-form';
    
    function Signin() {
      const {
        register,
        handleSubmit,
        formState: { errors },
      } = useForm();
    
      const onSubmit = (form) => {
        console.log(form);
      };
    
      return (
        <div>
          <form onSubmit={handleSubmit(onSubmit)}>
            <div>
              <label htmlFor="email">Email</label>
              <input type="email" id="email" {...register('email')} />
            </div>
            <div>
              <label htmlFor="password">Password</label>
              <input type="password" id="password" {...register('password')} />
            </div>
            <div>
              <button>Signin</button>
            </div>
          </form>
        </div>
      );
    }
    
    export default Signin;
    
    アプリケーションを実行し、ブラウザコンソールを開き、フォームを送信します.次のようなイメージが表示されます.

    入力検証の追加


    私が空のパスワードでフォームを提出するならば、私は悪いUXである私のパスワードを入力するのを忘れたことを示すどんなエラーメッセージも得ません.

    その機能を達成するために、2つのパッケージをインストールする必要があります.@hookform/resolvers/yup and yup .
    yarn add @hookform/resolvers yup
    

    NOTE:
    yup is one of JavaScript schema Objects. It allows us to define a shape (structure) and validate a JavaScript object. You can also use other schema object like Joi, Zod, etc.


    さて、入力検証をフォームに追加しましょう.
    // pages/Signin.js
    
    // ...
    import { yupResolver } from '@hookform/resolvers/yup';
    import * as yup from 'yup';
    
    const schema = yup.object().shape({
      email: yup.string().email().required(),
      password: yup.string().required(),
    });
    
    function Signin() {
      const {
        register,
        handleSubmit,
        formState: { errors },
      } = useForm({ resolver: yupResolver(schema) });
    
    // ...
    }
    // ...
    
    今、私たちが空のパスワードでフォームを提出するならば、フォームが実際にエラーメッセージを投げて、フォームを提出しないので、我々はブラウザーコンソールでどんなメッセージも見ません.

    見ても、カーソルが自動的にエラーを生成する入力要素にフォーカスします.

    エラーメッセージの表示


    さて、フォームは入力値を検証できますが、十分ではありません.何が間違っているのかを表示する必要があります.したがって、ユーザは正しい値を入力することができます.
    // pages/Signin.js
    
    <div>
      <label htmlFor="email">Email</label>
      <input type="email" id="email" {...register("email")} />
      {errors.email?.message && <span>{errors.email.message}</span>}
    </div>
    <div>
      <label htmlFor="password">Password</label>
      <input type="password" id="password" {...register("password")} />
      {errors.password?.message && <span>{errors.password.message}</span>}
    </div>
    
    フォームを送信し、エラーメッセージを見ることができます.

    うーん、私はエラーメッセージは、ユーザーフレンドリーな十分ではないと思う?では、改善しましょう.

    エラーメッセージのカスタマイズ


    これが私がスキーマ検証のためにYUPを選ぶ理由です.このようなエラーメッセージを簡単にカスタマイズできます.
    // pages/Signin.js
    
    // ...
    const schema = yup.object().shape({
      email: yup
        .string()
        .email('Please provide a valid email address')
        .required('Please provide your email address'),
      password: yup.string().required('Please provide your password'),
    });
    // ...
    
    フォームを再度送信し、エラーメッセージが表示されます.

    ボーナス


    クライアント側のフォームの妥当性検査は十分ではありません.また、サーバー側でフォームを検証する必要があります.
    サーバーからエラーメッセージをフォームに表示したい場合に問題が発生します.幸いにも、我々は簡単に反応フック形式を使用して、この機能を行うことができます.
    我々はちょうど使用する必要がありますsetError サーバーからフォームに来るエラーメッセージを表示するAPI.
    // pages/Signin.js
    
    // Response from the server
    // {
    //   "message": "...",
    //   "errors": {
    //     email: ["The email must be a valid email address."]
    //   }
    // }
    
    // ...
    const {
      register,
      handleSubmit,
      formState: { errors },
      setError,
    } = useForm({ resolver: yupResolver(schema) });
    
    const onSubmit = async (form) => {
      // Do sign-in process. Just example :)
      await Http.post(/** ... */);
    
      if ((await Http.status()) === 422) {
        let res = await Http.response();
    
        for (let [field, messages] of Object.entries(res.errors)) {
          setError(field, { type: 'manual', message: message.join(' ') });
        }
      }
    
      // ...
    };
    // ...