Created
October 28, 2024 01:12
-
-
Save danecando/94bed0af724db270fee847500e8ae1bf to your computer and use it in GitHub Desktop.
Sprinkle client side state/validation on server action form
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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("/"); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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