Un formulaire React garde la valeur de chaque champ dans l'état : le champ affiche value={state} et le met à jour avec onChange. Quand l'utilisateur envoie le formulaire, un gestionnaire onSubmit appelle e.preventDefault() pour empêcher le rechargement de la page, puis utilise les valeurs déjà présentes dans l'état.
Tapez un nom et appuyez sur Entrée ou cliquez sur le bouton. Supprimez ensuite la ligne e.preventDefault(); : l'aperçu montre les champs que le navigateur aurait envoyés lors d'un envoi natif, qui dans une vraie page la rechargerait aussi.
Un champ dont la valeur vient de l'état s'appelle un champ contrôlé. L'alternative, laisser le DOM garder la valeur et la lire plus tard, est traitée dans composants contrôlés et non contrôlés.
Champs texte et textarea
Chaque champ texte fonctionne de la même manière : value vient de l'état, onChange lit e.target.value. Un <textarea> suit le même pattern. En HTML, son texte se place entre les balises, mais en React vous le passez avec value.
Comme vous définissez la valeur vous-même, vous pouvez modifier ce que l'utilisateur a tapé avant que cela s'affiche : le champ titre met chaque lettre en majuscule. Le compteur de caractères se met à jour à chaque frappe, car body est un état et chaque modification provoque un nouveau rendu.
Select
Un <select> prend value sur l'élément select lui-même, pas selected sur une option. e.target.value est la value de l'option choisie.
Pour un select multiple, ajoutez multiple et passez un tableau dans value. Lisez les choix avec Array.from(e.target.selectedOptions, (o) => o.value).
Cases à cocher et boutons radio
Une case à cocher se contrôle avec checked, pas value, et son gestionnaire lit e.target.checked, qui vaut true ou false. Les boutons radio d'un groupe partagent un name ; chacun est coché quand l'état est égal à sa valeur.
Lire e.target.value sur une case à cocher est une erreur courante : pour une case sans attribut value, cela renvoie la chaîne "on", que la case soit cochée ou non.
Un seul objet d'état pour plusieurs champs
Un formulaire de dix champs n'a pas besoin de dix appels à useState et de dix gestionnaires. Gardez un seul objet, donnez à chaque champ un name qui correspond à une clé, et écrivez un seul gestionnaire qui utilise le nom comme clé calculée : [e.target.name].
Le gestionnaire vérifie le type du champ pour que la case à cocher stocke un booléen et les autres du texte. Ajoutez un champ en ajoutant une clé à l'objet de départ et un input avec le même name ; le gestionnaire n'a pas besoin de changer. Le spread conserve tous les autres champs (voir mettre à jour tableaux et objets).
Envoyer un formulaire
Placez la logique d'envoi sur le onSubmit du formulaire, pas sur le onClick du bouton. onSubmit se déclenche aussi quand l'utilisateur appuie sur Entrée dans un champ texte, ce qu'un gestionnaire de clic manquerait. Un <button> dans un formulaire est par défaut un bouton d'envoi ; donnez aux autres boutons type="button" pour qu'ils n'envoient pas le formulaire.
Ouvrez la console, tapez un mot de passe et appuyez sur Entrée : le formulaire affiche l'objet entier. Cliquer sur "Clear" n'affiche rien, car il est type="button". Retirez cet attribut et Clear se met aussi à envoyer le formulaire.
Validation et affichage des erreurs
Une validation simple est une fonction qui prend les valeurs et renvoie un objet de messages. Exécutez-la à l'envoi, stockez le résultat dans l'état et affichez chaque message sous son champ. Désactivez le bouton d'envoi pendant qu'une requête est en cours pour qu'un double clic n'envoie pas le formulaire deux fois.
Cliquez sur Save avec les champs vides pour voir les deux messages, puis remplissez-les. saveProfile remplace une vraie requête : elle attend 800 millisecondes puis se résout. L'attribut noValidate désactive les bulles de validation du navigateur pour que seuls vos messages s'affichent ; retirez-le et ajoutez required à un champ pour comparer.
Les erreurs peuvent aussi être calculées pendant le rendu au lieu d'être stockées, par exemple const emailError = form.email && !form.email.includes('@'), ce qui affiche le message pendant la saisie. Choisissez le moment qui convient au formulaire : à l'envoi, c'est plus calme ; pendant la saisie, le retour est plus rapide.
Actions de formulaire dans React 19
React 19 ajoute une autre façon de gérer un formulaire. Passez une fonction à la prop action du formulaire. React empêche le rechargement de la page pour vous et appelle la fonction avec un objet FormData contenant chaque champ qui a un name. Les champs n'ont besoin d'aucun état : ils sont non contrôlés, et React les vide une fois l'action terminée.
Tapez votre propre commentaire et publiez-le. La liste affiche votre texte, et le champ revient à Nice docs : une fois l'action résolue, React remet les champs non contrôlés à leur defaultValue. L'action peut être asynchrone, et pendant son exécution React traite la mise à jour comme une transition.
Pour afficher un état d'attente ou une erreur de l'action, enveloppez-la avec useActionState, qui renvoie le dernier résultat, une action enveloppée pour le formulaire et un indicateur isPending. Un bouton placé profondément dans le formulaire peut aussi lire l'état d'attente avec useFormStatus de react-dom :
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}
useFormStatus n'est pas chargé dans les éditeurs de cette page, il n'apparaît donc que dans ce bloc statique. Il doit être appelé depuis un composant rendu à l'intérieur du <form>, pas dans le composant qui affiche le formulaire.
L'état contrôlé avec onSubmit et les actions de formulaire avec FormData fonctionnent tous deux dans React 19. Les champs contrôlés conviennent aux formulaires qui réagissent pendant la saisie (validation en direct, compteur de caractères, champs qui dépendent les uns des autres). Les actions conviennent aux formulaires dont vous n'avez besoin des valeurs qu'au moment de l'envoi.
Questions fréquentes
Comment récupérer la valeur d'un input en React ?
Stockez-la dans l'état et mettez-la à jour depuis onChange : <input value={name} onChange={e => setName(e.target.value)} />. Le texte actuel est alors toujours dans name. Pour les cases à cocher, lisez e.target.checked au lieu de e.target.value.
Comment empêcher un formulaire de recharger la page en React ?
Appelez e.preventDefault() au début du gestionnaire onSubmit. Sans cela, le navigateur effectue un envoi de formulaire normal, qui navigue et recharge la page.
Comment gérer plusieurs inputs avec un seul état en React ?
Gardez un seul objet dans l'état, donnez à chaque champ un name qui correspond à une clé, et utilisez un seul gestionnaire avec une clé calculée : setForm({ ...form, [e.target.name]: e.target.value }).
Pourquoi un textarea utilise-t-il value au lieu de children en React ?
En HTML, le texte d'un textarea se place entre ses balises, mais en React vous le passez avec value (ou defaultValue), comme pour un input. Ainsi, tous les champs texte d'un formulaire React fonctionnent de la même façon.
Que sont les actions de formulaire dans React 19 ?
Dans React 19, vous pouvez passer une fonction à <form action={fn}>. React empêche le rechargement de la page, appelle la fonction avec le FormData du formulaire et réinitialise les champs non contrôlés à la fin. useActionState ajoute par-dessus un indicateur d'attente et un résultat.