Zustand・Zod・useRef を使ったステップ型フォームの実装
Zustand で状態管理し、Zod でバリデーションを行いながら、React の useRef を活用してステップ型フォームを作成する方法を説明します。
1. 必要なライブラリのインストール
yarn add zustand zod @hookform/resolvers react-hook-form
2. Zustand を使ったフォームの状態管理
Zustand を使って、現在のステップとフォームデータを管理します。
import { create } from "zustand";
interface FormState {
step: number;
data: {
name: string;
email: string;
password: string;
};
setStep: (step: number) => void;
updateData: (data: Partial<FormState["data"]>) => void;
}
export const useFormStore = create<FormState>((set) => ({
step: 1,
data: {
name: "",
email: "",
password: "",
},
setStep: (step) => set({ step }),
updateData: (data) => set((state) => ({ data: { ...state.data, ...data } })),
}));
3. Zod を使ったバリデーションスキーマ
各ステップごとにバリデーションスキーマを作成します。
import { z } from "zod";
export const step1Schema = z.object({
name: z.string().min(2, "名前は2文字以上で入力してください"),
});
export const step2Schema = z.object({
email: z.string().email("正しいメールアドレスを入力してください"),
});
export const step3Schema = z.object({
password: z.string().min(6, "パスワードは6文字以上必要です"),
});
4. 各ステップのフォームコンポーネント
React Hook Form を使い、Zod でバリデーションを適用します。
Step1: 名前入力
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { step1Schema } from "./schemas";
import { useFormStore } from "./store";
export default function Step1() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(step1Schema),
});
const { updateData, setStep } = useFormStore();
const onSubmit = (values: { name: string }) => {
updateData(values);
setStep(2);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>名前:</label>
<input {...register("name")} />
{errors.name && <p>{errors.name.message}</p>}
<button type="submit">次へ</button>
</form>
);
}
Step2: メールアドレス入力
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { step2Schema } from "./schemas";
import { useFormStore } from "./store";
export default function Step2() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(step2Schema),
});
const { updateData, setStep } = useFormStore();
const onSubmit = (values: { email: string }) => {
updateData(values);
setStep(3);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>メールアドレス:</label>
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
<button type="button" onClick={() => setStep(1)}>戻る</button>
<button type="submit">次へ</button>
</form>
);
}
Step3: パスワード入力
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { step3Schema } from "./schemas";
import { useFormStore } from "./store";
export default function Step3() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(step3Schema),
});
const { updateData, setStep } = useFormStore();
const onSubmit = (values: { password: string }) => {
updateData(values);
alert("フォーム送信完了!");
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>パスワード:</label>
<input type="password" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
<button type="button" onClick={() => setStep(2)}>戻る</button>
<button type="submit">送信</button>
</form>
);
}
5. ステップごとにフォームを切り替える
Zustand の step を監視しながら適切なコンポーネントを表示します。
import { useFormStore } from "./store";
import Step1 from "./Step1";
import Step2 from "./Step2";
import Step3 from "./Step3";
export default function MultiStepForm() {
const { step } = useFormStore();
return (
<div>
<h1>ステップ型フォーム</h1>
{step === 1 && <Step1 />}
{step === 2 && <Step2 />}
{step === 3 && <Step3 />}
</div>
);
}
6. useRef を活用してフォーカスを制御
フォーム送信後に次の入力フィールドへフォーカスを移動する場合、useRef を使って制御できます。
例えば Step1.tsx で useRef を使う場合:
import { useRef, useEffect } from "react";
export default function Step1() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <input ref={inputRef} />;
}
この方法を各ステップに適用すると、ユーザーがスムーズに入力を進められます。
まとめ
• Zustand: ステップと入力データの管理
• Zod: 各ステップのバリデーション
• React Hook Form: フォームのハンドリング
• useRef: フォーカス制御
この実装で、シンプルかつ拡張性の高いステップ型フォームが作成できます!