📑
Server Actionsを使ったフォームで通知を出す方法を考える
方法
- onSubmitを使う
onSubmitの中で関数を呼び出し、成否に応じてtoastを実行する。
フォームの処理中を判断するために、useTransitionを使う。
onSubmitを使ったフォームの実装
form.tsx
"use client";
import { Button, Loader, TextInput } from "@mantine/core";
import { useForm, zodResolver } from "@mantine/form";
import { useTransition } from "react";
import { toast } from "sonner";
import type { z } from "zod";
import { action } from "~/app/actions";
import { formSchema } from "~/app/schema";
export function Form() {
const [isPending, startTransition] = useTransition();
const form = useForm({
mode: "uncontrolled",
initialValues: {
email: "",
message: "",
},
validate: zodResolver(formSchema),
validateInputOnBlur: true,
});
const handleSubmit = (values: z.infer<typeof formSchema>) => {
startTransition(async () => {
try {
await action(values);
toast.success("success!");
} catch {
toast.error("error!");
}
});
};
return (
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
<TextInput
required
withAsterisk
label="Email"
placeholder="your@email.com"
name="email"
type="email"
key={form.key("email")}
{...form.getInputProps("email")}
/>
<TextInput
required
withAsterisk
label="Message"
placeholder="message"
name="message"
type="text"
key={form.key("message")}
{...form.getInputProps("message")}
/>
<Button disabled={isPending} type="submit">
{isPending && <Loader size="sm" mr="md" />}
送信
</Button>
</form>
);
}
- useEffectを使う
useEffectを使ってuseActionStateのstateから判定してtoastを実行する。
useEffectを使ったフォームの実装
form.tsx
"use client";
import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { Button, Loader, TextInput } from "@mantine/core";
import { useActionState, useEffect } from "react";
import { toast } from "sonner";
import { formAction } from "~/app/actions";
import { formSchema } from "~/app/schema";
export function Form() {
const [lastResult, action, isPending] = useActionState(formAction, undefined);
const [form, fields] = useForm({
constraint: getZodConstraint(formSchema),
lastResult,
onValidate({ formData }) {
return parseWithZod(formData, { schema: formSchema });
},
shouldValidate: "onBlur",
});
useEffect(() => {
if (lastResult && lastResult.status === "error") {
toast("error");
} else if (lastResult && lastResult.status === "success") {
toast("success");
}
}, [lastResult]);
return (
<form {...getFormProps(form)} action={action}>
<TextInput
required
withAsterisk
label="Email"
placeholder="your@email.com"
{...getInputProps(fields.email, { type: "email" })}
error={fields.email.errors}
/>
<TextInput
required
withAsterisk
label="Message"
placeholder="message"
{...getInputProps(fields.message, { type: "text" })}
error={fields.message.errors}
/>
<Button disabled={isPending} type="submit">
{isPending && <Loader size="sm" mr={4} />}
送信
</Button>
</form>
);
}
Discussion