今、Webアプリを作っているのですが、
その実装中にinputタグのバリデーションではまったので、
採用した方法について書きたいと思います。
調べてみた限り、
react-hook-formが主流?みたいですが、
あんまり実装したくなかったので、他の方法を探しました。
構成
フォルダ
- app
- _services
- components
- InputText.tsx
- page.tsx
{
"name": "paleo-web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest"
},
"dependencies": {
"@types/node": "20.3.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"autoprefixer": "10.4.14",
"eslint": "8.43.0",
"eslint-config-next": "13.4.7",
"next": "13.4.7",
"postcss": "8.4.24",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.2",
"typescript": "5.1.3"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
"@types/jest": "^29.5.5",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0"
}
}
inputタグ(InputText.tsx)
親コンポーネントからバリデーションのパターンと
そのパターン検証に失敗した場合のエラーメッセージを渡しています。
以下は数字に限定しています。
<InputText id="age" label="年齢" value={age} placeholder=""
validationPattern="[0-9]*" validationErrorMessage="数値で入力してください"
onChange={onChangeAge}></InputText>
CSSはTailwind CSSですが、そこでpeerを使います。
詳細はここにも書いていますが、inputのclassNameにpeerを設定して、
peer-invalid:visibleをエラーメッセージ表示のUI(今回の場合はspan)に設定します。
これだけでpatternに設定した検証に失敗したらspanのメッセージが表示されます。
<div className="flex flex-col">
<label htmlFor={props.id} className="text-sm font-bold text-gray-700 tracking-wide">
{props.label}
</label>
<input
id={props.id}
type="text"
placeholder={props.placeholder}
className="px-4 py-2 mt-2 text-base text-gray-700 placeholder-gray-600 border rounded-lg focus:outline-none focus:shadow-outline peer"
value={props.value}
pattern={props.validationPattern}
onChange={props.onChange}
/>
<span className="text-red-500 text-xs invisible peer-invalid:visible">{props.validationErrorMessage}</span>
</div>
<input
id={props.id}
type="text"
placeholder={props.placeholder}
className="px-4 py-2 mt-2 text-base text-gray-700 placeholder-gray-600 border rounded-lg focus:outline-none focus:shadow-outline peer"
value={props.value}
pattern={props.validationPattern}
onChange={props.onChange}
/>
<span className="text-red-500 text-xs invisible peer-invalid:visible">{props.validationErrorMessage}</span>
親コンポーネト側(page.tsx)
親コンポーネント(page.tsx)では
OnChangeイベントで、inputのイベントがくるので、
それの!event.target.validity.validでinputがエラー状態か見れるので、
それを使ってます。親で色々実装したくなかったので、この方法にしました。
※子供でpatternでバリデーションして、親でもバリデーションのコードを書くのが嫌だった
ValidityStateはブラウザのバージョンによっては使えないかもしれません。
私のアプリは使えるブラウザしかサポートしません笑
const [buttonDisabledAge, setButtonDisabledAge] = useState(true);
const onChangeAge = (event: React.ChangeEvent<HTMLInputElement>): void => {
setButtonDisabledAge(!event.target.validity.valid);
setAge(event.target.value);
}
あとはbuttonDisabledAgeの値によって、ボタンの表示・非表示が切り替わります。
<button disabled={buttonDisabledAge} className="rounded-full disabled:bg-gray-500 bg-blue-500 p-4 text-white hover:bg-blue-700 hover:shadow-xl peer-invalid:disabled"
onClick={handleClick}>計算する</button>
以上、誰かの参考になれば!