✅ はじめに
こんにちは!このシリーズでは、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:フォームの状態管理とコンテキスト