5.4 useZodValidatedForm
5.4 useZodValidatedForm
De useZodValidatedForm hook wordt gebruikt om een React Hook Form te integreren met Zod voor client-side validatie. Daarnaast wordt deze ook gebruikt om een formulier terug op te vullen na een mislukte inzending (HTTP/Next maakt dit standaard leegt).
We beginnen met het returntype van de hook te beschrijven, dit is een array met drie elementen:
- Het eerste element is het React Hook Form dat teruggegeven wordt door de useForm hook, net zoals in de Form & FormError appendix, moeten we ook hier drie generische parameters toevoegen. Merk op dat we de input en output helpers uit zod gebruiken om de generische parameters te typen. De input helper neemt een schema als generische parameter een geeft een type terug dat de geldige invoer voor dit schema beschrijft. De output helper neemt dezelfde argumenten, maar geeft het resultaat van transformaties op het schema terug. Aangezien we daar geen gebruik van maken in deze cursus, zal het resultaat van de input en output helper steeds hetzelfde zijn.
- Het tweede element is de functie die aan de action property van een <form> tag of van de Form component gekoppeld kan worden.
- Het derde element is de returnwaarde van een server action, i.e. een FormActionResponse.
import type {UseFormReturn} from 'react-hook-form'
import type {input, output} from 'zod'
type UseZodValidatedFormReturn<Schema extends ZodObject, ReturnType> = [
UseFormReturn<input<Schema>, unknown, output<Schema>>,
(payload: FormData) => void,
FormActionResponse<ReturnType>,
]Vervolgens definiëren we een hook die een zod schema, server action en eventuele parameters voor de useForm als parameters verwacht. Om de parameters van de useForm hook te typen, maken we gebruik van de UseFormProps interface uit React Hook Form waaraan we opnieuw de drie generische parameters moeten doorgeven.
In de body van de functie gebruiken we de useActionState zoals beschreven in hoofdstuk 1 en hoofdstuk 6. Daarnaast gebruiken we de useForm hook zoals besproken in hoofdstuk 6. Tenslotte geven we het resultaat van beide hooks terug.
import type {Path, UseFormProps, UseFormReturn} from 'react-hook-form'
import {useForm} from 'react-hook-form'
import type {ZodObject} from 'zod/v4'
import {useActionState, useEffect} from 'react'
import type {FormAction, FormActionDispatch, FormActionResponse} from '@/models/serverFunctions'
import {standardSchemaResolver} from '@hookform/resolvers/standard-schema'
const initialFormState = {
success: false,
}
export function useZodValidatedForm<Schema extends ZodObject, ReturnType>(
schema: Schema,
action: FormAction<ReturnType>,
props?: UseFormProps<input<Schema>, unknown, output<Schema>>,
): UseZodValidatedFormReturn<Schema, ReturnType> {
const [state, dispatch] = useActionState(action, initialFormState as FormActionResponse<ReturnType>)
const form = useForm<input<Schema>, unknown, output<Schema>>({
resolver: standardSchemaResolver(schema),
...props,
})
return [form, dispatch, state.data as FormActionResponse<ReturnType>]
}In het geval dat het formulier ingezonden wordt, maar dat de server side validatie mislukt, wordt de ingezonden data teruggestuurd door de server action (via de protectedFormAction en publicFormAction hooks). Het formulier wordt echter leeg gemaakt wat niet aangenaam is voor de gebruikers.
Om dit probleem op te lossen voegen we een effect toe dat getriggerd wordt als de state die teruggegeven wordt door useActionState van waarde wijzigt.
Als dat gebeurd, vullen we het formulier terug op via de reset methode van het form object. Daarnaast stellen we de error state van het formulier ook in. Hiervoor gebruiken we de setError methode van het form object.
We onderscheiden twee soorten errors, enerzijds zijn er veld-specifieke errors, die kunnen we instellen door de naam van het veld mee te geven als eerste parameter aan de setError methode. De andere errors zijn globale errors, errors die niet te koppelen zijn aan een specifiek veld. Het gaat dan meestal om interne server fouten, dit zijn de errors die onderaan in de publicFormAction en protectedFormAction terug gegeven worden (zie appendix 5.2). Dit soort errors moet gekoppeld worden aan het root veld in React Hook Form.
Tenslotte kunnen we de errors meegeven als een object aan de tweede parameter van de setError functie. Dit object heeft twee properties. De eerste property bepaald het soort error, aangezien we deze manueel hebben toegevoegd, zetten we deze property op manual. De twee property bevat de eigenlijk foutboodschap, hier concateneren we alle waarden in de error array die door Zod gegenereerd wordt naar één comma-seperated string.
export function useZodValidatedForm<Schema extends ZodObject, ReturnType>(
schema: Schema,
action: FormAction<ReturnType>,
props?: UseFormProps<input<Schema>, unknown, output<Schema>>,
): UseZodValidatedFormReturn<Schema, ReturnType> {
const [state, dispatch] = useActionState(action, initialFormState as FormActionResponse<ReturnType>)
const form = useForm<input<Schema>, unknown, output<Schema>>({
resolver: standardSchemaResolver(schema),
...props,
})
useEffect(() => {
if (state?.submittedData) {
// Reset the form with the data which was returned from the server.
form.reset(state.submittedData as input<Schema>)
}
if (state?.errors) {
Object.keys(state.errors).forEach(field =>
form.setError(field === 'errors' ? 'root' : (field as Path<input<Schema>>), {
type: 'manual',
message: state.errors![field]?.join(', '),
}),
)
}
}, [state, form])
return [form, dispatch, state.data as FormActionResponse<ReturnType>]
}