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
結果
感想
サンプルソースで簡単に導入できると思っていたが・・・
「@material-ui」の場合は違うattributeを指定しないと動かないとか・・・
ハマるって!!
導入できたからよしとする