【Astro ✖️ React Hook Form】確認画面付きのフォームを作る
はじめに
Astro で確認画面付きのフォームを作る方法です。
バリデーションは React Hook Form と Zod、確認画面へのデータの受け渡しは sessionStrage を使用します。
完成物
※ 送信機能は実装していません。
ファイル構成
|- components
| |- Form.tsx
| |- Confirm.tsx
|
|- pages
| |- index.astro
| |- confirm.astro
| |- thanks.astro
ライブラリのインストール
Astro で React を使用するため、@astrojs/react をインストールします。
npx astro add react
バリデーション用の React Hook Form と Zod をインストールします。
npm install react-hook-form zod @hookform/resolvers
入力画面
components/Form.tsx
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as zod from "zod";
// バリデーション設定
const schema = zod.object({
name: zod.string().min(1, { message: "必須項目です" }),
email: zod.string().email({ message: "有効なメールアドレスを入力してください" })
});
// フォームの入力内容の型
type FormData = {
name: string;
email: string;
};
const Form = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
//送信するデータをsessionStorageに保存
sessionStorage.setItem('contactData', JSON.stringify(data));
window.location.href = '/confirm';
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="name">
名前
</label>
<input
type="text"
id="name"
{...register("name")}
placeholder="山田 太郎" />
{errors.name && <p>{errors.name.message}</p>}
</div>
<div>
<label htmlFor="email">
メールアドレス
</label>
<input
type="email"
id="email"
{...register("email")}
placeholder="sampla@abc.com" />
{errors.email && <p>{errors.email.message}</p>}
</div>
<button type="submit">
確認画面へ
</button>
</form>
)
}
export default Form;
バリデーションは、React Hook Form と Zod を使用しています。
pages/index.astro
---
import Layout from "../layouts/Layout.astro";
import FormComponent from "../components/Form.tsx";
---
<Layout>
<FormComponent client:load />
</Layout>
.astro ファイルで、React コンポーネントを表示します。
コンポーネントの呼び出しにはclient:load
を追加します。
確認画面にデータを受け渡す
const onSubmit = (data: FormData) => {
//送信するデータをsessionStorageに保存
sessionStorage.setItem("contactData", JSON.stringify(data));
window.location.href = "/confirm";
};
確認画面へ遷移するボタンをクリックしたら、sessionStorage に送信するデータを保存して/confirm
に遷移します。
確認画面
components/Confirm.tsx
import { useState, useEffect } from 'react';
import { useForm } from "react-hook-form";
// フォームの入力内容の型定義
type FormData = {
name: string;
email: string;
};
const Confirm = () => {
const { register, handleSubmit } = useForm<FormData>({});
const [contactData, setContactData] = useState<Partial<FormData>>({});
useEffect(() => {
// sessionStorageからデータを取得
const sessionData = sessionStorage.getItem('contactData');
if (!sessionData) {
// データがない場合はフォームに戻る
window.location.href = '/';
} else {
setContactData(JSON.parse(sessionData));
}
}, []);
const onSubmit = async (data: FormData) => {
try {
// 送信処理を記述する
// 送信後の処理
// sessionStorageのデータを削除して/thanksに遷移
sessionStorage.removeItem('contactData');
window.location.href = '/thanks';
} catch (e) {
alert("送信に失敗しました。ネットワーク状況を確認してください");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<p>名前</p>
<p>{contactData.name}</p>
<input
type="hidden"
id="name"
{...register("name")}
value={contactData.name}
/>
</div>
<div>
<p>メールアドレス</p>
<p>{contactData.email}</p>
<input
type="hidden"
id="email"
{...register("email")}
value={contactData.email}
/>
</div>
<button
type="submit"
>
送信する
</button>
<button
type="button"
onClick={() => history.back()}
>
入力画面に戻る
</button>
</form>
);
};
export default Confirm;
pages/confirm.astro
---
import Layout from "../layouts/Layout.astro";
import ConfirmComponent from "../components/Confirm.tsx";
---
<Layout>
<ConfirmComponent client:load />
</Layout>
.astro ファイルで、React コンポーネントを表示します。
コンポーネントの呼び出しにはclient:load
を追加します。
sessionStrage からデータを取得する
const [contactData, setContactData] = useState<Partial<FormData>>({});
useEffect(() => {
// sessionStorageからデータを取得
const sessionData = sessionStorage.getItem("contactData");
if (!sessionData) {
// データがない場合はフォームに戻る
window.location.href = "/";
} else {
setContactData(JSON.parse(sessionData));
}
}, []);
setContactData(JSON.parse(sessionData));
で sessionStrage に保存したデータを json 形式で取得します。
また、/confirm に直接アクセスされた時に sessionStorage にデータがなければ入力画面に遷移させます。
送信内容を表示する
<div>
<p>名前</p>
<p>{contactData.name}</p>
<input
type="hidden"
id="name"
{...register("name")}
value={contactData.name}
/>
</div>
<div>
<p>メールアドレス</p>
<p>{contactData.email}</p>
<input
type="hidden"
id="email"
{...register("email")}
value={contactData.email}
/>
</div>
{contactData.name}
のように各データを表示出来ます。
送信用にtype="hidden"
の input タグのvalue
にも各データを格納します。
送信する
const onSubmit = async (data: FormData) => {
try {
// 送信処理を記述する
// 送信後の処理
// sessionStorageのデータを削除して/thanksに遷移
sessionStorage.removeItem("contactData");
window.location.href = "/thanks";
} catch (e) {
alert("送信に失敗しました。ネットワーク状況を確認してください");
}
};
送信処理が完了後に、sessionStorage の内容を削除して、/thanks
に遷移させます。
終わりに
Astro に React を導入してフォームを作成しました。
React の導入もコマンドひとつで簡単に出来ますし、React Hook Form を使用すればフォーム周りの機能も簡単に実装可能です。
今回は実装していませんが、フォームの送信機能は Google Form と GAS を使用することで全て無料で作成も可能です。
Discussion