masalibの日記

システム開発、運用と猫の写真ブログです

「react-hook-form」を入れてみた

SignUpのフォームを作ったがテストをしているとまともなバリデーションができていない事がわかった。

前々から気なっていた「react-hook-form」を導入してみた

react-hook-formとは

フォームのバリデーション(入力チェック)をするためのライブラリーです。 最小のパターンだと以下の形です

import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./index.css";

function App() {
  const { register, handleSubmit, errors } = useForm();
  const onSubmit = (data) => {
    console.log(JSON.stringify(data));
  };
  return (
    <div className="App">
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="firstName">First Name</label>
          <input
            name="firstName"
            placeholder="First Name"
            ref={register({ required: true })}
          />
          {errors.firstName?.type === "required" && (
            <div style={{ color: "red" }}>何か文字を入力してください</div>
          )}
        </div>
        <input type="submit" />
      </form>
    </div>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

結果は以下のとおりです

インストール

npm install react-hook-form

導入方法について

宣言する

useFormを宣言する

import { useForm } from "react-hook-form";
・
・
const { register, handleSubmit, errorste } = useForm();

submitの部分にかぶせる

フォームでサブミットしている部分に「handleSubmit」をいれる

- onClick={handleSignup}
+ onClick={handleSubmit(handleSignup)}

submitで動かす処理でイベント(event)を修正

-     async function handleSignup (event) {
-         event.preventDefault()      
+     async function handleSignup (data) {  //react-hook-formを導入したためevent -> dataに変更
+       //event.preventDefault()      //react-hook-formを導入したため削除

inputにバリデーションの部分を追加する

  <TextField
        error={state.isError}
        fullWidth
        id="email"
+       name="email"
        type="email"
        label="Email"
        placeholder="Email"
        margin="normal"
        onChange={handleEmailChange}
        onKeyPress={handleKeyPress}
+       inputRef={register({pattern: /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]{1,}\.[A-Za-z0-9]{1,}$/ })}
  />

nameも設定していなかったので設定しました

エラー時メッセージを表示

{errors.email?.type === "pattern" &&
<div style={{ color: "red" }}>メールアドレスの形式で入力されていません</div>}

サブミット以外で強制的にエラーハンドリングする場合

Enterキーで動くサブミット処理にもいれてみたのですが
うまくエラーハンドルしてくれない・・・

      const handleKeyPress = (event: React.KeyboardEvent) => {
          if (event.keyCode === 13 || event.which === 13) {
            console.log("handleKeyPress 13 enter")
           state.isButtonDisabled || handleSubmit(handleSignup());
          }
      }

どうやら同期処理じゃないとうまくいかないみたい・・・ でもonKeyPressのイベントで起動する関数を同期処理にさせるのはどうかと思った

handleKeyPress内の一部を同期処理する事にした

const handleKeyPress = (event: React.KeyboardEvent) => {
    if (event.keyCode === 13 || event.which === 13) {
      console.log("handleKeyPress 13 enter")

      if (!state.isButtonDisabled){
        handleKeyPresstrigger()
        if (errors) {
          //errorメッセージを表示する
        } else {
          handleSignup()  
        }
      }
    }
};
async function handleKeyPresstrigger () {
    const result = await trigger();
    return result
}

triggerは現在入っている内容でエラーハンドリングを起動する関数です。
もしエラーならerrorsに内容が入ってtrueになりSignUpに行きません。

@material-uiに入れる場合の注意点

@material-uiはinputタグを直接に書かない。
そのため「ref」のattribute(属性)が効かない。

エラーになるパターン

<TextField
    error={state.isError}
    fullWidth
    id="email"
    name="email"
    type="email"
    label="Email"
    placeholder="Email"
    margin="normal"
    onChange={handleEmailChange}
    onKeyPress={handleKeyPress}
    ref={register({pattern: /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]{1,}\.[A-Za-z0-9]{1,}$/ })}
/>

「📋 Field is missing name attribute」 というエラーがConsole.logに表示される

成功するパターン

<TextField
    error={state.isError}
    fullWidth
    id="email"
    name="email"
    type="email"
    label="Email"
    placeholder="Email"
    margin="normal"
    onChange={handleEmailChange}
    onKeyPress={handleKeyPress}
    inputRef={register({pattern: /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]{1,}\.[A-Za-z0-9]{1,}$/ })}
/>

ソース
https://react-hook-form.com/jp/advanced-usage#ControlledmixedwithUncontrolledComponents

結果

f:id:masalib:20201123111144p:plain

感想

サンプルソースで簡単に導入できると思っていたが・・・
「@material-ui」の場合は違うattributeを指定しないと動かないとか・・・

ハマるって!!

導入できたからよしとする