2025年5月26日月曜日

【第6回】バリデーション結果をモーダルで表示するReact Hook Form実装

✅ はじめに

こんにちは!このシリーズでは、React + Vite + TypeScript + MUIを使ってモダンなWebアプリを開発する方法をステップバイステップで解説しています。

第6回となる今回は、「バリデーション結果をモーダルで表示するReact Hook Form実装」に取り組みます。前回作成したフォームをベースに、バリデーションエラーや送信内容の確認結果をモーダル(ダイアログ)で表示するUIを実装していきます。

📦 使用する技術スタック

ツール バージョン 説明
React Hook Form v7.x 高性能なフォームバリデーションライブラリ
MUI v5.x マテリアルデザインベースのUIコンポーネント
TypeScript 5.x 型安全なJavaScript
Vite 5.x 高速な開発環境とビルドツール

🚀 実装手順

1. プロジェクトの作成

まずは、Viteを使って新しいプロジェクトを作成します:

npm create vite@latest react-form-modal -- --template react-ts
cd react-form-modal
npm install

2. 必要なパッケージのインストール

MUIとReact Hook Formをインストールします:

npm install @mui/material @emotion/react @emotion/styled @mui/icons-material react-hook-form @fontsource/roboto

3. モーダル付きフォームの作成

src/FormWithModal.tsxを作成します:

import { useForm } from "react-hook-form"
import {
  Box,
  Button,
  Container,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  TextField,
  Typography,
} from "@mui/material"
import { useState } from "react"

type FormValues = {
  name: string
  email: string
}

export default function FormWithModal() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormValues>({
    defaultValues: {
      name: "",
      email: "",
    },
  })

  const [open, setOpen] = useState(false)
  const [submittedData, setSubmittedData] = useState<FormValues | null>(null)

  const onSubmit = (data: FormValues) => {
    setSubmittedData(data)
    setOpen(true)
  }

  return (
    <Container maxWidth="sm">
      <Box mt={5}>
        <Typography variant="h5" gutterBottom>
          モーダル表示付きフォーム
        </Typography>
        <form onSubmit={handleSubmit(onSubmit)} noValidate>
          <Box display="flex" flexDirection="column" gap={2}>
            <TextField
              label="名前"
              fullWidth
              {...register("name", { required: "名前は必須です" })}
              error={Boolean(errors.name)}
              helperText={errors.name?.message}
            />
            <TextField
              label="メール"
              fullWidth
              {...register("email", {
                required: "メールは必須です",
                pattern: {
                  value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
                  message: "メール形式が不正です",
                },
              })}
              error={Boolean(errors.email)}
              helperText={errors.email?.message}
            />
            <Button type="submit" variant="contained" color="primary">
              送信
            </Button>
          </Box>
        </form>
      </Box>
      <Dialog open={open} onClose={() => setOpen(false)}>
        <DialogTitle>送信内容の確認</DialogTitle>
        <DialogContent dividers>
          {submittedData && (
            <Box>
              <Typography>名前: {submittedData.name}</Typography>
              <Typography>メール: {submittedData.email}</Typography>
            </Box>
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setOpen(false)} color="primary">
            閉じる
          </Button>
        </DialogActions>
      </Dialog>
    </Container>
  )
}

4. App.tsxの更新

最後に、src/App.tsxを更新してフォームを表示します:

import { ThemeProvider, createTheme } from '@mui/material'
import FormWithModal from "./FormWithModal"
import '@fontsource/roboto/300.css'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/500.css'
import '@fontsource/roboto/700.css'

const theme = createTheme({
  typography: {
    fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
  },
})

function App() {
  return (
    <ThemeProvider theme={theme}>
      <FormWithModal />
    </ThemeProvider>
  )
}

export default App

🎯 主要なポイント

1. モーダル管理

  • Dialog コンポーネント: MUIのモーダルダイアログを使用
  • 状態管理: useStateでモーダルの開閉を制御
  • 送信データ: 確認用モーダルに表示するデータを保持

2. フォーム機能

  • バリデーション: 名前とメールアドレスの入力検証
  • エラー表示: フィールド単位でのエラーメッセージ表示
  • 送信処理: フォームデータをモーダルで確認

3. UIコンポーネント

  • Dialog関連: タイトル、コンテンツ、アクションの3層構造
  • TextField: エラー状態とメッセージの統合
  • レイアウト: Boxコンポーネントによる柔軟な配置

🔚 まとめ

今回は、React Hook FormとMUIのDialogコンポーネントを組み合わせて、ユーザーフレンドリーなフォームを実装しました。バリデーション結果や送信内容をモーダルで表示することで、より分かりやすいUIを実現できました。

📚 次回予告

【第7回】React Hook Form × MUI:フォームをコンポーネント単位で分割する設計手法