Comment ajouter des validateurs custom sur react-hook-form

Maîtrisez les tests de formulaires avec React-Hook-Form, Yup.test ou Zod.refine et résolvez les problèmes de formState facilement.
Le formState de react-hook-form ne se met jamais à jour
const {
register,
handleSubmit,
formState
} = useForm<PostForm>({
resolver: zodResolver(postValidationSchema)
})
Vous est-il déjà arrivé d'avoir errors toujours vide avec votre resolver ?
Lorsque vous loggez formState dans un useEffect.
Commençons par vérifier notre resolver zod
Voici à quoi ressemble mon formulaire:
Voici le schéma zod:
import { ZodType, z } from "zod"
const isUrl =
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
export const postValidationSchema: ZodType<PostForm> = z
.object({
videoUrl: z
.string()
.refine((videoUrl) => {
if (
videoUrl.length > 0 &&
isUrl.test(videoUrl) &&
(!/youtube(.com|.fr|.co.jp|.co.uk|.be)/.test(videoUrl) ||
!/watch\?v=[a-zA-Z0-9_]{3,}/.test(videoUrl))
)
return false
return true
}, "must_be_valid_youtube")
.optional(),
message: z
.string()
.refine((message) => {
if (message.length > 0 && isUrl.test(message)) return false
return true
}, "no_url_in_message")
.optional()
})
.partial()
Avec ce schéma de validation, nous voulons mettre en place les règles suivantes:
- Un utilisateur doit au moins saisir une videoUrl ou un message
- La videoUrl est forcément une vidéo provenant de Youtube
- Le message ne doit contenir aucune url
Puis, nous ajoutons la vérification sur notre bouton de formulaire:
<button
type="submit"
disabled={!formState.isValid}
>
<IoIosSend className="mr-2 text-2xl" />
Send the message
</button>
Maintenant, même si ces règles sont appliqués, les messages d'erreur n'arrivent jamais au formulaire:
// que ce soit
{formState.errors?.message && (
<span className="text-red-700">
{translate(
`formState.errors.message.message`,
lang
)}
</span>
)}
// ou encore
{formState.errors?.videoUrl && (
<span className="text-red-700">
{translate(
`formState.errors.videoUrl.message`,
lang
)}
</span>
)}
Où sont passés mes messages d'erreurs ?
Lorsque l'on essait d'afficher les erreurs sur notre formulaire, React-Hook-Form se comporte très bizarrement. Mais rassurez vous, cela n'a rien à voir avec votre schéma zod ou yup.
Même si cela ne fonctionne pas encore, à ce stade vous n'êtes plus très loin !
Afficher les messages d'erreur React-Hook-Form correctement
Reprenons notre définition du formulaire:
import { PostForm, postValidationSchema } from "@/customTypes/post"
import { zodResolver } from "@hookform/resolvers/zod"
import { SubmitHandler, useForm } from "react-hook-form"
const {
register,
handleSubmit,
formState,
watch,
trigger
} = useForm<PostForm>({
resolver: zodResolver(postValidationSchema)
})
Cette fois-ci, nous allons ajouter watch et trigger.
Watch permettra de déclencher un changement de state à chaque modification de la valeur des champs. Nous pourrons l'utiliser dans un useEffect.
Trigger quant à lui, sert à forcer un passage au resolver, c'est un vrai douanier !
Utilisons-les comme ceci:
React.useEffect(() => {
const subscription = watch(() => {
trigger(["message", "videoUrl"])
})
return () => subscription.unsubscribe()
}, [watch, trigger])
Et maintenant, à quoi ressemble notre formulaire ?
J'ai l'impression que tout est ok 😊.
J'espère que cet article vous aidera car je me suis arraché les cheveux pendant 2 jours pour résoudre cette incohérence. Bon code !

Alexandre P.
Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.
Poursuivre la lecture dans la rubrique Dev