Conform - SSD Validacion Formularios
Requisitos
A la fecha 14/11/2024
https://www.youtube.com/watch?v=SJjP7zdYv_s&t=908s (opens in a new tab)
React 19 (canary)
next: 15
react 19.rc
Con estos el codigo utiliza:
import { useActionForm } from "react"
React 18
next: 15
react: 18
Con estos el codigo utiliza:
import { useFormState } from "react-dom"
Instalar zod y conform
Zod sirve para crear esquemas (schema) entypescript y conform es el utilitario para validar formularios www.conform.guide
# Conform
npm install @conform-to/react @conform-to/zod --legacy-peer-deps
#Zod
npm i zop --legacy-peer-deps
Crear Schema Zod
import { z } from "zod";
export const personSchema = z.object({
uuid: z.string().uuid(), // UUID type validation
names: z.string().min(2).max(80),
lastnameFirst: z.string().min(2).max(60),
});
Esquema alternativo con mesnajes personalizados
export const personValidationSchema = z.object({
uuid: z.string().uuid(), // UUID type validation
names: z.preprocess(
(value) =>(value === '' ? undefined : value),
z.string({ required_error: "Nombres es obligatorio" })
.min(2, 'Nombres debe tener al menos 2 caracteres'),
),
});
Crear Server Side Validation - action
"use server";
import { parseWithZod } from "@conform-to/zod";
import { redirect } from "next/navigation";
import { personSchema } from "../data/schema";
export async function registerPersonas (prevState: unknown, formData: FormData){
const submission = parseWithZod(formData, {
schema: personSchema,
});
if(submission.status !== 'success'){
return submission.reply();
}
return redirect("/dashboard/personas");
}
Crear Formulario
Formulario sin configuracion
"use client"
import { Button } from "@/components/ui/button"
import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export default function NewPersona() {
return (
<div className="col-span-2 grid items-start gap-6 lg:col-span-1">
<form className="grid gap-6" action={...} >
<Card className="">
<CardHeader>
<CardTitle>Registrar persona</CardTitle>
<CardDescription>
Complete el formulario para continuar
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="grid gap-2">
<Label htmlFor="names">Nombres</Label>
<Input placeholder="Primer nombre, segundo" />
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Apellido Paterno</Label>
<Input placeholder="Apellido paterno o primer apellido" />
</div>
</CardContent>
<CardFooter className="justify-between space-x-2">
<Button variant="ghost">Cancelar</Button>
<Button type="submit">Guardar</Button>
</CardFooter>
</Card>
</form>
</div>
)
}
Configurar Conform en el formulario
- Importar useActionForm - react 19 o useFormState
- Importar dependecnias
- Configurar useFormState ...
- Reemplazar la action x conform: useForm
- Actualizar Inputs con: key, name y defaultValue
- Agregar mensajes a Inputs
//otros imports
import { registerPersonas } from "./personsa_actions"
import { useFormState } from "react-dom"
import { useForm } from "@conform-to/react"
import { parseWithZod } from "@conform-to/zod"
import { personSchema } from "../data/schema"
export default function NewPersona() {
const [lastResult, action] = useFormState(registerPersonas, undefined);
const [form, fields] = useForm({
lastResult,
onValidate({formData}){
return parseWithZod(formData, {
schema: personSchema
})
},
shouldValidate: 'onBlur',
shouldRevalidate: 'onInput',
});
return (
<div className="col-span-2 grid items-start gap-6 lg:col-span-1">
{/* Agregar id al form, actualizar llamada a action: onSubmit */}
<form className="grid gap-6" id={form.id} action={action} onSubmit={form.onSubmit}>
<Card className="">
<CardHeader>
<CardTitle>Registrar persona</CardTitle>
<CardDescription>
Complete el formulario para continuar
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="grid gap-2">
<Label htmlFor="names">Nombres</Label>
{/* Actualizar key,name y defaultValue */}
<Input id="names"
key={fields.names.key}
name="{fields.names.name}"
defaultValue={fields.names.initialValue}
placeholder="Primer nombre, segundo" />
{/* Actualizar despliegue de errores/mensajes */}
<p className="text-xs text-muted-foreground text-red-500">{fields.names.errors}</p>
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Apellido Paterno</Label>
<Input id="subject"
key={fields.lastnameFirst.key}
name="{fields.lastnameFirst.name}"
defaultValue={fields.lastnameFirst.initialValue}
placeholder="Apellido paterno o primer apellido" />
<p className="text-xs text-muted-foreground text-red-500">{fields.lastnameFirst.errors}</p>
</div>
</CardContent>
<CardFooter className="justify-between space-x-2">
<Button variant="ghost">Cancelar</Button>
<Button type="submit">Guardar</Button>
</CardFooter>
</Card>
</form>
</div>
)
}