Maîtrise la gestion des formulaires en React avec les composants contrôlés, apprends à gérer l'état des champs de saisie, traiter les soumissions, et construire des formulaires interactifs.
En HTML, les éléments de formulaire comme <input>, <textarea>, et <select> maintiennent leur propre état interne — le navigateur garde la trace de ce que l'utilisateur a tapé. En React, nous voulons typiquement que l'état React soit la source unique de vérité pour les données du formulaire. C'est ce qu'on appelle un composant contrôlé.
Un champ de saisie contrôlé est un champ dont la valeur est pilotée par l'état React. Tu définis l'attribut value du champ avec une variable d'état et tu mets à jour cet état via un gestionnaire onChange :
function NameForm() {
const [name, setName] = React.useState('');
return (
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
);
}Avec ce modèle :
onChange.setName() avec la nouvelle valeur.Cela peut sembler être du travail supplémentaire, mais cela te donne un contrôle total sur les données du formulaire. Tu peux valider la saisie pendant que l'utilisateur tape, transformer les valeurs (par exemple, forcer les majuscules), empêcher conditionnellement des changements, ou synchroniser plusieurs champs.
Un composant non contrôlé est un composant où le DOM lui-même détient l'état. Tu accèdes à la valeur en utilisant une ref au lieu de la suivre dans l'état :
function UncontrolledForm() {
const inputRef = React.useRef(null);
function handleSubmit() {
alert(inputRef.current.value);
}
return <input ref={inputRef} type="text" />;
}Les composants non contrôlés sont plus simples pour les cas basiques, mais les composants contrôlés sont recommandés pour la plupart des formulaires React car ils maintiennent le flux de données prévisible.
Pour gérer la soumission de formulaire en React, attache un gestionnaire onSubmit à l'élément <form>. Appelle toujours e.preventDefault() pour empêcher le navigateur de rafraîchir la page (le comportement par défaut des formulaires HTML) :
function LoginForm() {
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
function handleSubmit(e) {
e.preventDefault();
console.log('Submitting:', { email, password });
// Envoyer les données à une API, valider, etc.
}
return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password:
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type="submit">Log In</button>
</form>
);
}Le gestionnaire onSubmit se déclenche quand l'utilisateur clique sur le bouton de soumission ou appuie sur Entrée pendant qu'il est concentré sur un champ à l'intérieur du formulaire. Utiliser onSubmit sur le formulaire (plutôt que onClick sur le bouton) est important car cela capture les deux méthodes de soumission.
Après la soumission, tu peux vider le formulaire en réinitialisant l'état :
function handleSubmit(e) {
e.preventDefault();
// Traiter les données...
setEmail('');
setPassword('');
}En HTML, <textarea> utilise ses enfants pour la valeur, et <select> utilise l'attribut selected sur <option>. En React, les deux sont contrôlés de la même manière que <input> — en utilisant value et onChange.
Textarea :
function CommentBox() {
const [comment, setComment] = React.useState('');
return (
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
rows={4}
placeholder="Write a comment..."
/>
);
}En React, <textarea> utilise la prop value au lieu d'enfants. Cela le rend cohérent avec <input>.
Select :
function ColorPicker() {
const [color, setColor] = React.useState('green');
return (
<select value={color} onChange={(e) => setColor(e.target.value)}>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
);
}Le value sur <select> détermine quelle option est sélectionnée. Tu n'as pas besoin de mettre un attribut selected sur les éléments <option> individuels — React le gère via la prop value.
Select multiple :
function MultiSelect() {
const [selected, setSelected] = React.useState([]);
function handleChange(e) {
const options = Array.from(e.target.selectedOptions);
setSelected(options.map((o) => o.value));
}
return (
<select multiple value={selected} onChange={handleChange}>
<option value="js">JavaScript</option>
<option value="py">Python</option>
<option value="go">Go</option>
</select>
);
}Quand ton formulaire a de nombreux champs, créer une variable d'état et un gestionnaire séparés pour chacun peut être fastidieux. Un modèle courant est d'utiliser un seul objet d'état et un seul gestionnaire qui utilise l'attribut name du champ pour déterminer quel champ mettre à jour :
function RegistrationForm() {
const [form, setForm] = React.useState({
username: '',
email: '',
age: '',
});
function handleChange(e) {
const { name, value } = e.target;
setForm((prev) => ({
...prev,
[name]: value,
}));
}
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={form.username}
onChange={handleChange}
placeholder="Username"
/>
<input
name="email"
type="email"
value={form.email}
onChange={handleChange}
placeholder="Email"
/>
<input
name="age"
type="number"
value={form.age}
onChange={handleChange}
placeholder="Age"
/>
<button type="submit">Register</button>
</form>
);
}La technique clé est les noms de propriétés calculés : [name]: value. Cela définit dynamiquement la propriété correcte dans l'objet d'état en fonction du champ qui a déclenché l'événement de changement.
Pour les cases à cocher, utilise e.target.checked au lieu de e.target.value :
function handleChange(e) {
const { name, value, type, checked } = e.target;
setForm((prev) => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
}La validation de formulaire garantit que les utilisateurs fournissent des données valides avant la soumission. Tu peux valider à la soumission, au changement (pendant que l'utilisateur tape), ou au blur (quand l'utilisateur quitte un champ).
Validation à la soumission :
function SignupForm() {
const [email, setEmail] = React.useState('');
const [error, setError] = React.useState('');
function handleSubmit(e) {
e.preventDefault();
if (!email.includes('@')) {
setError('Merci de saisir une adresse email valide');
return;
}
setError('');
console.log('Submitted:', email);
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Sign Up</button>
</form>
);
}Validation au changement (retour en temps réel) :
function PasswordInput() {
const [password, setPassword] = React.useState('');
const isValid = password.length >= 8;
return (
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<p style={{ color: isValid ? 'green' : 'red' }}>
{isValid ? 'Le mot de passe est assez fort' : 'Le mot de passe doit contenir au moins 8 caractères'}
</p>
</div>
);
}Désactiver le bouton de soumission jusqu'à ce que le formulaire soit valide est un modèle UX courant :
<button type="submit" disabled={!email || !password}>
Submit
</button>Pour les applications en production, considère l'utilisation d'une bibliothèque de formulaires comme React Hook Form ou Formik pour une validation complexe, mais comprendre les fondamentaux des composants contrôlés est essentiel avant d'utiliser ces outils.
<div id="root"></div>
<script src="https://unpkg.com/react@19/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@19/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState } = React;
function App() {
const [input, setInput] = useState('');
const [items, setItems] = useState(['Learn React', 'Practice forms']);
function handleSubmit(e) {
e.preventDefault();
if (input.trim()) {
setItems([...items, input.trim()]);
setInput('');
}
}
return (
<div>
<h2>Todo List</h2>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Add a task..."
/>
<button type="submit">Add</button>
</form>
<ul>
{items.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>Dans un champ de saisie contrôlé, qu'est-ce qui détermine la valeur affichée ?