Menu

Composants contrôlés et non contrôlés en React

Un champ contrôlé reçoit sa valeur de l'état React et ne change que par onChange. Un champ non contrôlé garde sa propre valeur dans le DOM, et vous la lisez avec une ref ou FormData. Découvrez la différence, les avertissements courants et quand utiliser chacun.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un composant contrôlé est un élément de formulaire dont la valeur vit dans l'état React : vous passez value et mettez à jour l'état dans onChange, c'est donc React qui décide de ce que le champ affiche. Un composant non contrôlé garde sa valeur dans le DOM, comme en HTML classique : vous passez une defaultValue de départ et lisez la valeur actuelle plus tard via une ref ou FormData.

Tapez dans les deux champs. Le paragraphe suit le premier à chaque frappe, car chaque modification met à jour l'état et provoque un nouveau rendu. Le second champ ne change que dans le DOM ; React ne fait aucun rendu, et vous ne voyez sa valeur que lorsque "Read both" la demande.

Champs contrôlés

Avec un champ contrôlé, l'état React est la seule source de vérité. À chaque frappe, le déroulement est le suivant :

  1. L'utilisateur tape, et le navigateur déclenche un événement input.
  2. Votre gestionnaire onChange lit e.target.value et appelle le setter.
  3. React refait le rendu et réécrit le nouvel état dans la value du champ.

Comme votre code se trouve au milieu de cette boucle, vous pouvez faire ce qu'un champ non contrôlé ne permet pas : refuser ou transformer des caractères, afficher la valeur ailleurs, désactiver un bouton tant que la valeur n'est pas valide, ou définir la valeur depuis le code (un bouton « Effacer », une suggestion sur laquelle l'utilisateur clique).

Essayez de taper des lettres : elles n'apparaissent jamais, car le gestionnaire retire tout ce qui n'est pas un chiffre avant de mettre à jour l'état. Le bouton Verify reste désactivé tant qu'il n'y a pas exactement six chiffres.

Les champs contrôlés sont ce qu'utilisent les exemples de la page sur les formulaires, et c'est le choix par défaut dans la plupart du code React.

Champs non contrôlés

Un champ non contrôlé fonctionne comme en HTML classique. React l'affiche une fois avec defaultValue (ou defaultChecked pour les cases à cocher et les boutons radio), puis le laisse tranquille. Pour lire la valeur, gardez une ref vers l'élément ou lisez tous les champs nommés du formulaire à l'envoi avec FormData.

Il n'y a ni état pour les champs ni onChange. Les valeurs ne sont recueillies qu'à l'envoi du formulaire. Décochez la case et envoyez à nouveau : la clé subscribe disparaît, car une case décochée ne fait pas du tout partie de FormData.

Modifier defaultValue après le premier rendu n'a aucun effet sur le champ, puisque React ne l'applique qu'à la création de l'élément. Pour réinitialiser un formulaire non contrôlé, appelez e.target.reset(), ou donnez au formulaire une nouvelle key pour que React crée des champs neufs.

Un champ dans lequel on ne peut pas taper

Si vous passez value sans onChange, le champ est contrôlé, mais rien ne met jamais l'état à jour. React réécrit la même valeur après chaque frappe, et le champ semble figé.

Essayez de taper dans chaque champ. Le premier ne change pas. Le deuxième est la correction pour un champ contrôlé, et quand vous tapez dedans, le premier et le dernier champ le suivent, car tous trois lisent le même état. Le troisième accepte la saisie car il est non contrôlé. Le quatrième indique volontairement qu'il ne peut pas être modifié.

En développement, React affiche un avertissement pour le premier champ : "You provided a value prop to a form field without an onChange handler". L'aperçu fonctionne comme un build de production, vous ne verrez donc pas cet avertissement ici, mais vous le verrez sur votre propre serveur de développement.

Passer de non contrôlé à contrôlé

React décide si un champ est contrôlé en regardant value. Si value vaut undefined ou null, le champ est non contrôlé. Un champ dont la value vaut d'abord undefined puis devient une chaîne passe donc de non contrôlé à contrôlé au cours de sa vie, et React avertit en développement : "A component is changing an uncontrolled input to be controlled".

L'avertissement signale un vrai bug. Le chemin inverse, d'une chaîne vers undefined, rend le champ à nouveau non contrôlé, et React cesse d'y écrire.

Tapez quelque chose dans les deux champs et appuyez sur chaque bouton Clear. Le second champ se vide. Le premier garde son texte : mettre l'état à undefined l'a rendu non contrôlé, React ne contrôle donc plus ce qu'il affiche.

La source habituelle de undefined est une donnée pas encore chargée (value={user.name} avant l'arrivée de l'utilisateur) ou un champ d'objet qui n'existe pas encore. Deux corrections :

const [name, setName] = useState('');   // start with a string
<input value={user?.name ?? ''} />       // or fall back to one

Pour les cases à cocher, la même règle s'applique à checked : initialisez-le à false, pas à undefined.

Les champs fichier sont toujours non contrôlés

Un <input type="file"> ne peut pas être contrôlé, car les navigateurs ne laissent pas une page définir sa valeur (une page capable de choisir des fichiers sur votre disque serait une faille de sécurité). Lisez les fichiers choisis dans e.target.files dans onChange, ou via une ref à l'envoi du formulaire.

Choisissez un ou deux fichiers sur votre ordinateur ; rien n'est envoyé, l'exemple ne fait qu'afficher les noms et les tailles. La seule valeur que vous pouvez définir sur un champ fichier est la chaîne vide, et c'est ainsi que le bouton Clear le réinitialise.

Que choisir

ContrôléNon contrôlé
La valeur vit dansL'état ReactLe DOM
Définir la valeur de départvalue / checkeddefaultValue / defaultChecked
Lire la valeurÀ tout moment, depuis l'étatAvec une ref, ou FormData à l'envoi
Rendu à chaque frappeOuiNon
Modifier la valeur depuis le codeMettre à jour l'étatÉcrire dans le DOM via une ref
Adapté àValidation en direct, mise en forme, champs dépendantsFormulaires simples lus à l'envoi, champs fichier, actions de formulaire

Choisissez contrôlé quand l'écran doit réagir à la valeur pendant la saisie. Choisissez non contrôlé quand vous n'avez besoin des valeurs qu'à la fin, ce qui est aussi le fonctionnement des actions de formulaire de React 19 : <form action={fn}> donne un FormData à votre fonction et réinitialise ensuite les champs non contrôlés.

Faire un rendu à chaque frappe semble coûteux, mais pour un formulaire normal ce n'est pas un problème. Cela le devient seulement quand un arbre volumineux et lent refait son rendu avec le champ ; gardez alors l'état près du champ, ou déplacez la partie lente dans son propre composant.

Les deux mêmes termes décrivent aussi vos propres composants. Un composant qui reçoit sa valeur et un onChange de son parent par les props est contrôlé par le parent ; un composant qui garde son propre état est non contrôlé. Faire remonter l'état pour rendre un composant contrôlé, c'est le sujet de faire remonter l'état.

Questions fréquentes

Qu'est-ce qu'un composant contrôlé dans React ?

Un élément de formulaire dont la valeur actuelle vient de l'état React, définie avec value (ou checked) et mise à jour par onChange. L'état React est la seule source de vérité, vous connaissez donc toujours la valeur et pouvez la modifier depuis le code.

Qu'est-ce qu'un composant non contrôlé dans React ?

Un élément de formulaire qui garde sa propre valeur dans le DOM, comme en HTML classique. Vous lui donnez une valeur de départ avec defaultValue (ou defaultChecked) et lisez la valeur actuelle quand vous en avez besoin, via une ref ou FormData à l'envoi.

Pourquoi ne puis-je pas taper dans mon input React ?

Vous avez passé value sans onChange. Le champ est contrôlé, donc React remet la valeur de l'état après chaque frappe. Ajoutez un onChange qui met à jour l'état, passez à defaultValue, ou ajoutez readOnly s'il ne doit vraiment pas changer.

Comment corriger "A component is changing an uncontrolled input to be controlled" ?

La value du champ valait d'abord undefined (ou null) puis est devenue une chaîne. Assurez-vous qu'elle soit toujours une chaîne : initialisez l'état à '', ou écrivez value={name ?? ''}.

Faut-il utiliser des champs contrôlés ou non contrôlés ?

Utilisez des champs contrôlés quand l'interface réagit à la valeur pendant la saisie : validation, mise en forme, bouton désactivé, champs qui dépendent les uns des autres. Utilisez des champs non contrôlés quand vous n'avez besoin des valeurs qu'à l'envoi, et pour les champs fichier, qui sont toujours non contrôlés.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER