Débutant25 min de lecture

Remonter l'État (Lifting State Up)

Apprends à partager l'état entre composants frères en le remontant vers leur parent commun, permettant ainsi un comportement coordonné dans ton arbre de composants.

Quand des Composants Frères Doivent Partager des Données

Dans React, les données circulent vers le bas du parent vers l'enfant via les props. On appelle cela le flux de données unidirectionnel ou flux de données à sens unique. Mais que se passe-t-il quand deux composants frères doivent partager et réagir aux mêmes données ?

Considère un convertisseur de température avec deux champs de saisie — un pour les Celsius et un pour les Fahrenheit. Quand l'utilisateur tape dans le champ Celsius, l'affichage Fahrenheit doit se mettre à jour, et vice versa. Les deux composants ont besoin d'accéder à la même valeur de température.

Tu ne peux pas passer de données directement entre frères. À la place, tu remontes l'état partagé vers leur plus proche parent commun. Le parent détient l'état et le transmet aux deux enfants via les props.

html
     Parent (détient l'état)
      /         \
  EnfantA      EnfantB
  (lit)        (lit)

Ce pattern s'appelle remonter l'état (lifting state up) et c'est l'un des patterns les plus fondamentaux dans React. Quand tu as besoin de synchroniser deux composants, cherche leur parent commun et déplace l'état là-bas.

Avant de remonter l'état (cassé) :

jsx
// Chaque composant a son propre état — ils ne peuvent pas communiquer
function CelsiusInput() {
  const [celsius, setCelsius] = React.useState(0);
  return <input value={celsius} onChange={...} />;
}

function FahrenheitDisplay() {
  // Comment ce composant peut-il connaître la valeur Celsius ? Il ne peut pas !
  return <p>???</p>;
}

Après avoir remonté l'état (fonctionnel) :

jsx
function App() {
  const [celsius, setCelsius] = React.useState(0);
  return (
    <>
      <CelsiusInput celsius={celsius} onCelsiusChange={setCelsius} />
      <FahrenheitDisplay celsius={celsius} />
    </>
  );
}

Maintenant le parent possède les données et les deux enfants peuvent y accéder.

Remonter l'État vers le Parent

Voici le processus étape par étape pour remonter l'état :

Étape 1 : Identifier l'état partagé. Détermine quel état doit être partagé entre les composants. Dans notre exemple de température, c'est la valeur de température.

Étape 2 : Trouver le parent commun. Trouve le composant le plus proche dans l'arbre qui est un ancêtre de tous les composants ayant besoin de l'état partagé.

Étape 3 : Déplacer l'état vers le parent. Retire l'état des composants enfants et ajoute-le au parent en utilisant useState.

Étape 4 : Passer l'état vers le bas via les props. Passe la valeur de l'état à chaque enfant qui a besoin de la lire.

Étape 5 : Passer la fonction de mise à jour comme prop. Passe la fonction de mise à jour (ou un gestionnaire qui l'appelle) à l'enfant qui doit modifier l'état.

Voici l'exemple complet :

jsx
function TemperatureConverter() {
  // Étape 3 : L'état vit dans le parent
  const [celsius, setCelsius] = React.useState(0);

  return (
    <div>
      {/* Étapes 4 & 5 : Passer l'état et le modificateur vers le bas */}
      <CelsiusInput
        value={celsius}
        onChange={setCelsius}
      />
      <FahrenheitDisplay celsius={celsius} />
    </div>
  );
}

function CelsiusInput({ value, onChange }) {
  return (
    <label>
      Celsius:
      <input
        type="number"
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
      />
    </label>
  );
}

function FahrenheitDisplay({ celsius }) {
  const fahrenheit = celsius * 9 / 5 + 32;
  return <p>{fahrenheit.toFixed(1)}°F</p>;
}

Le CelsiusInput ne possède pas l'état de température — il reçoit la valeur et appelle onChange quand l'utilisateur tape. Le parent reçoit la mise à jour, change son état, et les deux enfants se re-rendent avec la nouvelle valeur.

Passer des Fonctions de Mise à Jour comme Props

Quand un composant enfant doit modifier l'état du parent, le parent passe une fonction callback. C'est ainsi que les données circulent vers le haut dans React — via des callbacks.

jsx
function Parent() {
  const [items, setItems] = React.useState(['Item 1', 'Item 2']);

  function addItem(text) {
    setItems([...items, text]);
  }

  function removeItem(index) {
    setItems(items.filter((_, i) => i !== index));
  }

  return (
    <div>
      <AddItemForm onAdd={addItem} />
      <ItemList items={items} onRemove={removeItem} />
    </div>
  );
}

function AddItemForm({ onAdd }) {
  const [text, setText] = React.useState('');

  function handleSubmit(e) {
    e.preventDefault();
    if (text.trim()) {
      onAdd(text); // Appeler la fonction du parent
      setText('');
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button type="submit">Ajouter</button>
    </form>
  );
}

function ItemList({ items, onRemove }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>
          {item}
          <button onClick={() => onRemove(index)}>Retirer</button>
        </li>
      ))}
    </ul>
  );
}

Points clés :

  • L'enfant appelle le callback (onAdd, onRemove) fourni par le parent.
  • La fonction du parent met à jour l'état.
  • React re-rend le parent et les deux enfants avec le nouvel état.
  • L'enfant n'a pas besoin de savoir comment l'état est stocké ou mis à jour — il appelle simplement le callback.

Cela garde les composants enfants réutilisables. Ils fonctionnent avec n'importe quel parent qui fournit les props callback attendues.

Source Unique de Vérité

Le principe derrière la remontée d'état est la source unique de vérité. Pour toute donnée qui change dans ton application, il devrait y avoir exactement un composant qui la "possède" dans son état. Tous les autres composants qui ont besoin de ces données devraient les recevoir via les props.

Pourquoi est-ce important ?

  1. Pas d'incohérence de données. Si deux composants ont chacun leur propre copie des mêmes données, elles peuvent se désynchroniser. Avec une source unique, il n'y a qu'une seule valeur et elle est toujours cohérente.

  2. Débogage plus facile. Quand quelque chose ne va pas, tu n'as besoin de regarder qu'à un seul endroit pour trouver la source des données.

  3. Mises à jour prévisibles. Tous les changements d'état passent par un seul chemin, rendant le flux de données facile à tracer.

Exemple d'état dupliqué (mauvais) :

jsx
// Mauvais : les deux enfants ont leur propre copie de "name"
function Parent() {
  return (
    <>
      <NameInput />  {/* A son propre état name */}
      <Greeting />   {/* A son propre état name */}
    </>
  );
}
// Si NameInput se met à jour, Greeting ne le sait pas !

Exemple de source unique de vérité (bon) :

jsx
// Bon : le parent possède l'état, les enfants reçoivent via les props
function Parent() {
  const [name, setName] = React.useState('');
  return (
    <>
      <NameInput name={name} onNameChange={setName} />
      <Greeting name={name} />
    </>
  );
}
// Les deux enfants affichent toujours le même nom

Comment identifier où l'état devrait vivre :

  1. Identifie chaque composant qui rend quelque chose basé sur cet état.
  2. Trouve le plus proche parent commun de tous ces composants.
  3. Ce parent commun (ou un composant au-dessus de lui) devrait posséder l'état.

Patterns de Communication entre Composants

À mesure que ton application grandit, les composants doivent communiquer de différentes façons. Voici un résumé des principaux patterns :

Parent vers enfant (props) : Le pattern le plus simple. Le parent passe les données vers le bas via les props.

jsx
<UserCard name={user.name} email={user.email} />

Enfant vers parent (callbacks) : Le parent passe une fonction comme prop. L'enfant l'appelle pour envoyer des données vers le haut.

jsx
// Parent
function Parent() {
  function handleSelect(item) {
    console.log('Sélectionné:', item);
  }
  return <Menu onSelect={handleSelect} />;
}

// Enfant
function Menu({ onSelect }) {
  return <button onClick={() => onSelect('about')}>À propos</button>;
}

Frère vers frère (remontée d'état) : Remonte l'état partagé vers le parent commun et passe-le aux deux frères.

jsx
function Parent() {
  const [selected, setSelected] = React.useState(null);
  return (
    <>
      <Sidebar items={items} onSelect={setSelected} />
      <Detail selected={selected} />
    </>
  );
}

Quand la remontée d'état devient lourde : Si tu te retrouves à passer des props à travers de nombreuses couches de composants qui n'utilisent pas eux-mêmes les données (appelé prop drilling), il est peut-être temps de considérer :

  • React Context — fournit un moyen de partager des données sans passer les props à travers chaque niveau.
  • Bibliothèques de gestion d'état — comme Zustand ou Redux, pour un état complexe dont de nombreux composants ont besoin.

Ce sont des sujets plus avancés, mais la fondation est toujours la même : un composant possède l'état, et les autres composants y accèdent via une interface bien définie.

Remontée d'État 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 CelsiusInput({ value, onChange }) {
    return (
      <label>
        Celsius:
        <input
          type="number"
          value={value}
          onChange={(e) => onChange(Number(e.target.value))}
        />
      </label>
    );
  }

  function FahrenheitDisplay({ celsius }) {
    const fahrenheit = (celsius * 9) / 5 + 32;
    return <p>Fahrenheit: {fahrenheit.toFixed(1)}&deg;F</p>;
  }

  function App() {
    const [celsius, setCelsius] = useState(0);

    return (
      <div>
        <h2>Convertisseur de Température</h2>
        <CelsiusInput value={celsius} onChange={setCelsius} />
        <FahrenheitDisplay celsius={celsius} />
      </div>
    );
  }

  ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>

Quand deux composants frères doivent partager un état, où cet état devrait-il vivre ?

Prêt à pratiquer ?

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