✅ はじめに
こんにちは!このシリーズでは、React + Vite + TypeScript + MUIを使ってモダンなWebアプリを開発する方法をステップバイステップで解説しています。
第9回となる今回は、React Hook Form を使用して、フォーム入力内容をローカルストレージに保存・復元する機能を実装します。ユーザーが入力途中で画面を閉じても、次回アクセス時に入力内容を復元できる機能を作ります。
📦 使用する技術スタック
ツール | バージョン | 説明 |
---|---|---|
React Hook Form | v7.x | 高性能なフォームバリデーションライブラリ |
MUI | v5.x | マテリアルデザインベースのUIコンポーネント |
TypeScript | 5.x | 型安全なJavaScript |
Vite | 5.x | 高速な開発環境とビルドツール |
🎯 今回のゴール
- フォーム入力内容の自動保存機能の実装
- 保存したデータの復元機能の実装
- データのクリア機能の実装
🚀 実装手順
1. プロジェクトの作成
まずは、Viteを使って新しいプロジェクトを作成します:
npm create vite@latest react-form-storage -- --template react-ts cd react-form-storage npm install
2. 必要なパッケージのインストール
MUIとReact Hook Formをインストールします:
npm install @mui/material @emotion/react @emotion/styled @mui/icons-material react-hook-form
3. カスタムフックの作成
src/hooks/useLocalStorage.tsを作成します:
import { useState, useEffect } from "react"; export function useLocalStorage(key: string, initialValue: T) { // 初期値の設定 const [storedValue, setStoredValue] = useState (() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(error); return initialValue; } }); // 値の更新時にローカルストレージも更新 useEffect(() => { try { window.localStorage.setItem(key, JSON.stringify(storedValue)); } catch (error) { console.error(error); } }, [key, storedValue]); return [storedValue, setStoredValue] as const; }
4. フォームコンポーネントの作成
src/components/PersistentForm.tsxを作成します:
import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { Box, Button, Container, TextField, Typography, } from "@mui/material"; import { useLocalStorage } from "../hooks/useLocalStorage"; import type { FC } from "react"; type FormValues = { name: string; email: string; message: string; }; const STORAGE_KEY = "form_data"; const PersistentForm: FC = () => { const [storedData, setStoredData] = useLocalStorage>( STORAGE_KEY, {} ); const { register, handleSubmit, reset, watch, formState: { errors }, } = useForm ({ defaultValues: storedData, }); // フォームの値が変更されたら自動保存 useEffect(() => { const subscription = watch((value) => { setStoredData(value); }); return () => subscription.unsubscribe(); }, [watch, setStoredData]); const onSubmit = (data: FormValues) => { console.log("送信データ:", data); alert(JSON.stringify(data, null, 2)); // 送信成功時にフォームとストレージをクリア reset(); setStoredData({}); }; const handleClear = () => { reset(); setStoredData({}); }; 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 fullWidth label="名前" {...register("name", { required: "名前は必須です" })} error={Boolean(errors.name)} helperText={errors.name?.message} /> <TextField fullWidth label="メール" {...register("email", { required: "メールは必須です", pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "メール形式が不正です", }, })} error={Boolean(errors.email)} helperText={errors.email?.message} /> <TextField fullWidth label="メッセージ" multiline rows={4} {...register("message", { required: "メッセージは必須です", minLength: { value: 10, message: "10文字以上入力してください", }, })} error={Boolean(errors.message)} helperText={errors.message?.message} /> <Box display="flex" justifyContent="space-between" alignItems="center" > <Button variant="outlined" color="secondary" onClick={handleClear} > クリア </Button> <Button type="submit" variant="contained" color="primary"> 送信 </Button> </Box> </Box> </form> </Box> </Container> ); }; export default PersistentForm;
5. App.tsxの更新
最後に、src/App.tsxを更新してフォームを表示します:
import PersistentForm from "./components/PersistentForm"; import type { FC } from "react"; const App: FC = () => { return; }; export default App;
🎯 主要なポイント
1. カスタムフック設計
- 型安全性: ジェネリクスを使用した柔軟な型定義
- エラーハンドリング: try-catchによる安全な処理
- 永続化: useEffectによる自動保存の実装
2. フォーム機能
- 自動保存: watchによる入力値の変更検知
- データ復元: defaultValuesによる初期値の設定
- クリア機能: フォームとストレージの同時クリア
3. UX設計
- バリデーション: リアルタイムな入力検証
- フィードバック: エラーメッセージの表示
- 操作性: クリアと送信の2つのアクション
🔚 まとめ
今回は、React Hook Form と localStorage を組み合わせて、フォーム入力内容を永続化する機能を実装しました。 この実装により、以下のメリットが得られます:
- ✅ 入力内容の保持: ブラウザを閉じても入力内容が失われない
- 🔄 自動保存: 入力内容がリアルタイムで保存される
- 🗑️ データ管理: 必要に応じて入力内容をクリア可能
📚 次回予告
【第10回】React Hook Form:フォームの状態管理とコンテキスト