Form
離脱ガードの注意点
元々 Pages Router に存在した router.events が App Router から使えなくなった ので、ブラウザ標準のクリックイベントリスナーを使って離脱ガードを実装しています。将来的に router.events が復活したらそちらを使ってルーティングのキャンセルを行います。
"use client";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { Button } from "@/app/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/app/components/ui/form";
import { Input } from "@/app/components/ui/input";
import { useI18n } from "@/locales/client";
import { useToast } from "@/app/components/ui/use-toast";
import { useFormGuard } from "@/app/lib/form-guard";
export default function FormWidget() {
const t = useI18n();
const formSchema = z.object({
nickname: z
.string()
.min(1, {
message: t("formErrorMessages.required"),
})
.max(60, {
message: t("formErrorMessages.maxLength#othre", {
count: 60,
}),
}),
email: z
.string()
.min(1, {
message: t("formErrorMessages.required"),
})
.email({
message: t("formErrorMessages.invalid"),
}),
});
const { toast } = useToast();
const form = useForm<z.infer<typeof formSchema>>({
mode: "onChange",
resolver: zodResolver(formSchema),
defaultValues: {
nickname: "",
email: "",
},
});
const onSubmit = async (values: z.infer<typeof formSchema>) => {
return new Promise((resolve) => setTimeout(resolve, 2000)).then(() => {
form.reset();
toast({
title: t("submitted"),
});
});
};
useFormGuard(form.formState.isDirty);
return (
<>
<div className="py-6">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="nickname"
render={({ field }) => (
<FormItem>
<FormLabel>{t("nickname")}</FormLabel>
<FormControl>
<Input
type="text"
autoComplete="off"
placeholder=""
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>{t("email")}</FormLabel>
<FormControl>
<Input
type="email"
autoComplete="email"
placeholder=""
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
disabled={form.formState.isSubmitting || !form.formState.isValid}
type="submit"
>
{t("send")}
</Button>
</form>
</Form>
</div>
</>
);
}