Débutant25 min de lecture

Formulaires et composants contrôlés

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.

Champs de saisie contrôlés vs. non contrôlés

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 :

jsx
function NameForm() {
  const [name, setName] = React.useState('');

  return (
    <input
      type="text"
      value={name}
      onChange={(e) => setName(e.target.value)}
    />
  );
}

Avec ce modèle :

  1. L'utilisateur tape un caractère.
  2. Le navigateur déclenche l'événement onChange.
  3. Ton gestionnaire appelle setName() avec la nouvelle valeur.
  4. React re-rend le composant avec l'état mis à jour.
  5. Le champ affiche la nouvelle valeur depuis l'état.

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 :

jsx
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.

Gestion de la soumission de formulaire

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) :

jsx
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 :

jsx
function handleSubmit(e) {
  e.preventDefault();
  // Traiter les données...
  setEmail('');
  setPassword('');
}

Textarea et Select

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 :

jsx
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 :

jsx
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 :

jsx
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>
  );
}

Plusieurs champs avec un seul gestionnaire

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 :

jsx
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 :

jsx
function handleChange(e) {
  const { name, value, type, checked } = e.target;
  setForm((prev) => ({
    ...prev,
    [name]: type === 'checkbox' ? checked : value,
  }));
}

Bases de la validation de formulaire

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 :

jsx
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) :

jsx
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 :

jsx
<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.

Formulaires en action

html
<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 ?

Prêt à pratiquer ?

Crée ton compte gratuit pour accéder à l'éditeur de code interactif, lancer les défis et suivre ta progression.