React でフォームバリデーションの実装方法 (Joi Validation)
Reactでフォームバリデーションを実装する方法について、便利なライブラリを使って詳しく解説していきます。
フォームバリデーションは、ウェブアプリケーションにおいてユーザーからの入力が期待する形式であることを保証する重要なプロセスです。
このプロセスを効率的に管理するため、以下のライブラリを紹介します。
- Joi: Airbnb によって開発された、強力で信頼性の高いバリデーションライブラリです。
- react-hook-form: 効率的で柔軟なフォームハンドリングを可能にするReactライブラリです。
- @hookform/resolvers: Joiなどのバリデーションライブラリとreact-hook-formを簡単に統合できるアダプターです。
- react-bootstrap (オプション): スタイリッシュなフォームコンポーネントを提供するUIフレームワークです。
Joi Validation とは?
Joiは、データのバリデーションを容易にし、開発プロセスを加速させることができるライブラリです。
特に、フォームデータの検証において、その柔軟性と拡張性は大きなメリットを提供します。
Joiを使用することで、様々なデータ型に対する複雑なバリデーションルールを簡単に定義し、実装することができます。
特に、react-hook-formとJoiを組み合わせることで、コードの冗長性を減らし、フォームのバリデーションロジックを簡潔かつ効率的に実装できるようになります。
さらに、react-bootstrapをオプションとして取り入れることで、フォームの見た目を簡単にカスタマイズして、ユーザーにとって良いUIを提供することができます。
この記事のReactでのフォームバリデーションを使用して、信頼性の高いウェブアプリケーション開発ができるかと思います。
React VITE のセットアップ
まずは React VITE をインストール
npm create vite@latest
bootstrap のインストール(オプション)
npm i react-bootstrap bootstap
react-hook-form と Joi をつなげる @hookform/resolvers。
npm i joi react-hook-form @hookform/resolvers
ファイル構成
src -- App.tsx -- App.css
プロジェクト内容
CSS ファイル
コードを開く
h2 {
text-shadow: 0px 15px 5px rgba(0, 0, 0, 0.1),
10px 20px 5px rgba(0, 0, 0, 0.05),
-10px 20px 5px rgba(0, 0, 0, 0.05);
}
body {
background-color: rgba(0, 0, 0, 0.2);
}
input[type="text"],
input[type="file"] {
box-shadow: rgba(50, 50, 93, 0.25) 0px 30px 60px -12px inset, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px inset;
}
input[type='range']::-webkit-slider-runnable-track {
height: 2.1rem;
border: 1px solid lightgray;
box-shadow: rgba(50, 50, 93, 0.25) 0px 30px 60px -12px inset, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px inset;
}
input[type='range']::-webkit-slider-thumb {
margin-top: 0rem;
width: 2rem;
height: 2rem;
background: black;
}
App.jsx
バリデーションの設定は下記の通りです
- moodScore: mood の状態が1の状態では送信不可
- why: Reason のフォームが空の状態、もしくは3文字以内 & 200文字以上では送信不可
const schema = Joi.object({
moodScore: Joi.number().min(1).max(4).required(),
why: Joi.string().min(3).max(200).required()
});
react-hook-form の watch で、 useEffectを使うことなく状態を監視できます。
const { handleSubmit, control, watch, formState: { errors } } = useForm({
resolver: joiResolver(schema),
defaultValues: {
moodScore: 2, // 初期値
why: "Feeling a bit peckish 🍔" // 初期値
}
});
// watchを使用してmoodScoreの値を監視
const watchMoodScore = watch('moodScore');
return (
...
...
{/* watchMoodScoreの値に応じて絵文字を表示 */}
<div className="text-center my-3" style={{ fontSize: "3rem" }}>
{scoreToEmoji(Number(watchMoodScore))}
</div>
コードを全部開く
import { useForm, Controller } from 'react-hook-form';
import { joiResolver } from '@hookform/resolvers/joi';
import Joi from 'joi';
import { Button, Form } from 'react-bootstrap';
import './App.css';
import "bootstrap/dist/css/bootstrap.min.css";
// Joiのバリデーションをスキーマを作成
const App = () => {
const schema = Joi.object({
moodScore: Joi.number().min(1).max(4).required(),
why: Joi.string().min(3).max(200).required()
});
// useFormフックを使用してフォームの状態を管理
const { handleSubmit, control, watch, formState: { errors } } = useForm({
resolver: joiResolver(schema),
defaultValues: {
moodScore: 2, // 初期値
why: "Feeling a bit peckish 🍔" // 初期値
}
});
// watchを使用してmoodScoreの値を監視
const watchMoodScore = watch('moodScore');
// moodScoreに応じて絵文字を返す関数
const scoreToEmoji = (score) => {
switch (score) {
case 0: return "😭";
case 1: return "😔";
case 2: return "😐";
case 3: return "🙂";
case 4: return "😃";
default: return "🤷♂️";
}
};
// フォームのデータを送信する関数
async function onSubmit(data) {
try {
window.alert(data.why);
} catch (error) {
console.error(error);
}
}
return (
<div className='w-50 mx-auto mt-5'>
<h2 className="text-center">Your Mood Today</h2>
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group>
<Form.Label>Mood Indicator</Form.Label>
<Controller
name="moodScore"
control={control}
render={({ field }) => <Form.Range min={0} max={4} {...field} />}
/>
{/* バリデーションのエラーメッセージを表示 */}
{errors.moodScore && <Form.Text className="text-danger">悲しい気持ちをシェアしないでください</Form.Text>}
{/* watchMoodScoreの値に応じて絵文字を表示 */}
<div className="text-center my-3" style={{ fontSize: "3rem" }}>{scoreToEmoji(Number(watchMoodScore))}</div>
</Form.Group>
<Form.Group>
<Form.Label>Reason</Form.Label>
<Controller
name="why"
control={control}
render={({ field }) => <Form.Control as="textarea" rows={3} {...field} />}
/>
{/* バリデーションのエラーメッセージを表示 */}
{errors.why && <Form.Text className="text-danger">{errors.why.message}</Form.Text>}
</Form.Group>
<Button variant="success" type="submit" className="mt-4" >Share Your Mood</Button>
</Form>
</div>
);
};
export default App;
人気記事
Kazu
オーストラリア在住歴7年。現地で英語学習後に日本語教師 → 工場就職 → 海外WEB開発ディプロマを取得→フリーランス。現在は余った時間をプログラミング&ブログに費やして生きています。
136