0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zustand・Zod・useRef を使ったステップ型フォームの実装

Last updated at Posted at 2025-02-14

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: フォーカス制御

この実装で、シンプルかつ拡張性の高いステップ型フォームが作成できます!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?