Skip to content

Instantly share code, notes, and snippets.

@danecando
Created October 28, 2024 01:12
Show Gist options
  • Save danecando/94bed0af724db270fee847500e8ae1bf to your computer and use it in GitHub Desktop.
Save danecando/94bed0af724db270fee847500e8ae1bf to your computer and use it in GitHub Desktop.
Sprinkle client side state/validation on server action form
"use server";
import { redirect } from "next/navigation";
import { z } from "zod";
let signUpSchema = z.object({
email: z.string().min(1, "Email is required").email("Please enter a valid email address"),
displayName: z.string().min(2, "Display name must be at least 2 characters"),
password: z.string().min(6, "Password must be at least 6 characters"),
confirmation: z.string().min(6, "Password confirmation must be at least 6 characters"),
});
export async function signup(formData: FormData) {
"use server";
const validatedFields = signUpSchema.safeParse({
name: formData.get("name"),
email: formData.get("email"),
password: formData.get("password"),
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
};
}
redirect("/");
}
"use client";
import * as React from "react";
import { signup } from "@/app/actions/auth";
type FormState = Awaited<ReturnType<typeof signup>> & {
errors?: {
confirmPassword?: string;
};
};
export default function SignUpPage() {
let [state, setState] = React.useState<FormState>();
let [isPending, startTransition] = React.useTransition();
let submitAction = async (formData: FormData) => {
let password = formData.get("password");
let confirmation = formData.get("confirmPassword");
if (password !== confirmation) {
setState((s) => ({
...s,
errors: {
...s?.errors,
confirmPassword: "Passwords do not match",
},
}));
return;
}
startTransition(async () => {
let state = await signup(formData);
setState(state);
});
};
return (
<form action={submitAction}>
<div>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" />
</div>
{state?.errors?.email && <p>{state.errors.email}</p>}
<div>
<label htmlFor="displayName">Display Name</label>
<input id="displayName" name="displayName" type="text" />
</div>
{state?.errors?.displayName && <p>{state.errors.displayName}</p>}
<div>
<label htmlFor="password">Password</label>
<input id="password" name="password" type="password" />
</div>
{state?.errors?.password && (
<div>
<p>Password must:</p>
<ul>
{state.errors.password.map((error) => (
<li key={error}>- {error}</li>
))}
</ul>
</div>
)}
<div>
<label htmlFor="confirmPassword">Password</label>
<input id="confirmPassword" name="confirmPassword" type="password" />
</div>
{state?.errors?.confirmPassword && <p>{state.errors.confirmPassword}</p>}
<button type="submit" disabled={isPending}>
Sign Up
</button>
</form>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment