
Composants non contrôlés (Uncontrolled components) et `useRef` (Alternative)
Découvrez l'alternative aux composants contrôlés : les composants non contrôlés en React. Apprenez à utiliser `useRef` pour accéder aux valeurs des inputs directement depuis le DOM.
L'alternative : laisser le dom gérer l'état
Alors que les composants contrôlés sont l'approche recommandée et la plus courante pour gérer les formulaires en React, il existe une alternative : les **composants non contrôlés** (Uncontrolled Components). Comme leur nom l'indique, dans cette approche, React ne "contrôle" pas la valeur de l'input via son état.
Au lieu de cela, on laisse l'élément de formulaire HTML fonctionner comme il le ferait naturellement : le DOM gère lui-même la valeur interne de l'input au fur et à mesure des interactions de l'utilisateur. La source de vérité pour la valeur du champ réside donc directement dans le DOM, et non dans l'état du composant React.
Cela signifie que nous n'avons pas besoin de déclarer un état `useState` pour chaque champ, ni d'écrire une fonction `onChange` pour synchroniser l'état. Cela peut sembler plus simple au premier abord, mais pose alors la question : comment récupérer la valeur du champ lorsque nous en avons besoin, par exemple lors de la soumission du formulaire ?
Accéder aux valeurs du dom avec `useRef`
La réponse réside dans l'utilisation des **Refs**. Une Ref en React fournit un moyen d'accéder directement aux noeuds du DOM (ou aux instances de composants React). Le hook `useRef` est l'outil principal pour créer une Ref dans un composant fonctionnel.
Pour utiliser une Ref avec un composant non contrôlé :
1. **Créer une Ref :** Utilisez `const myInputRef = useRef(null);` pour initialiser une Ref.
2. **Attacher la Ref :** Passez cette Ref à l'attribut `ref` de votre élément de formulaire : ``.
3. **Accéder à la valeur :** Lorsque vous avez besoin de la valeur (typiquement dans le gestionnaire `onSubmit`), vous pouvez y accéder via la propriété `current` de la Ref, puis la propriété `value` du noeud DOM : `myInputRef.current.value`.
La Ref agit comme un pointeur direct vers l'élément DOM sous-jacent, vous permettant de lire sa valeur actuelle à la demande.
Exemple d'implémentation avec `useRef`
Voici comment implémenter un formulaire simple avec un composant non contrôlé en utilisant `useRef` :
import React, { useRef } from 'react';
function UncontrolledForm() {
// 1. Créer une ref pour l'input
const nameInputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
// 3. Accéder à la valeur via ref.current.value
const enteredName = nameInputRef.current.value;
alert('Nom soumis (non contrôlé) : ' + enteredName);
console.log('Valeur via ref:', enteredName);
// Optionnel: on peut aussi manipuler le DOM directement (moins recommandé)
// nameInputRef.current.value = ''; // Pour réinitialiser, mais moins "Reactif"
};
return (
);
}
export default UncontrolledForm;Notez l'absence de `useState` pour la valeur de l'input et de `onChange` pour la synchronisation. La valeur initiale peut être fournie via l'attribut `defaultValue` (ou `defaultChecked` pour les checkboxes/radios), qui n'est utilisé par React que lors du premier rendu.
Quand utiliser les composants non contrôlés ? Cas d'usage et compromis
Les composants non contrôlés peuvent être avantageux dans certaines situations :
- **Simplicité pour les formulaires très simples :** Si vous avez juste besoin de récupérer une valeur à la soumission sans validation complexe ni interaction en temps réel, ils nécessitent moins de code.
- **Intégration avec du code non-React :** Si vous intégrez du code React dans une application existante ou utilisez des bibliothèques jQuery qui manipulent directement le DOM.
- **Optimisation des performances (cas rares) :** Eviter les mises à jour d'état à chaque frappe peut théoriquement être plus performant, mais c'est rarement un goulot d'étranglement significatif et les composants contrôlés offrent plus de flexibilité.
- **Gestion des inputs de type `file` :** Comme nous le verrons, les inputs de fichiers sont intrinsèquement non contrôlés.
Cependant, ils présentent des inconvénients :
- **Moins de contrôle :** Il est plus difficile d'implémenter une validation instantanée, un formatage en temps réel, ou de désactiver conditionnellement le bouton de soumission basé sur les valeurs actuelles.
- **Flux de données moins clair :** La source de vérité est dans le DOM, ce qui s'écarte du modèle déclaratif habituel de React centré sur l'état.
- **Manipulation directe du DOM :** Bien que possible via la Ref (par exemple, pour réinitialiser), cela est généralement considéré comme un anti-pattern en React.
En général, pour la plupart des formulaires dans une application React, l'approche des **composants contrôlés est préférable** car elle offre plus de flexibilité, un meilleur contrôle et s'intègre plus naturellement au paradigme de React.
Le cas spécifique de ``
Un cas où les composants non contrôlés sont obligatoires est celui de l'input de type `file` (``). Pour des raisons de sécurité, sa valeur (la liste des fichiers sélectionnés) est en lecture seule et ne peut pas être définie de manière programmatique par JavaScript. Par conséquent, vous ne pouvez **pas** en faire un composant contrôlé.
Vous devez obligatoirement utiliser une Ref pour accéder aux fichiers sélectionnés par l'utilisateur, généralement dans le gestionnaire `onSubmit`.
import React, { useRef } from 'react';
function FileUploadForm() {
const fileInputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
// Accéder aux fichiers via ref.current.files
const files = fileInputRef.current.files;
if (files.length > 0) {
console.log('Fichier sélectionné:', files[0]);
alert(`Fichier soumis : ${files[0].name}`);
// Logique pour envoyer le fichier (par exemple, avec FormData)
// const formData = new FormData();
// formData.append('myFile', files[0]);
// fetch('/api/upload', { method: 'POST', body: formData });
} else {
alert('Veuillez sélectionner un fichier.');
}
};
return (
);
}
export default FileUploadForm;La propriété `files` de l'élément DOM est un objet `FileList` (similaire à un tableau) contenant les objets `File` sélectionnés par l'utilisateur.