Kodokon kodokon.com

Sous le capot : réconciliation, batching, rendu concurrent

Découvrez comment Fiber réconcilie l'arbre, groupe les mises à jour et rend le travail interruptible.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

Depuis React 16, le moteur interne s'appelle Fiber : chaque élément de votre arbre est représenté par un nœud fiber, une unité de travail. React maintient en réalité deux arbres (technique du double buffering) : l'arbre current, qui reflète ce qui est affiché, et l'arbre workInProgress, construit pendant le rendu. Le travail se déroule en deux phases distinctes : la phase de rendu (appel de vos composants, calcul des différences), interruptible et rejouable, puis la phase de commit (application des mutations au DOM, exécution des effets), toujours synchrone et ininterruptible. La réconciliation repose sur deux heuristiques qui ramènent la comparaison à un coût linéaire : si le type d'un élément change à une position donnée, React détruit tout le sous-arbre et le reconstruit, état compris ; pour les listes, la prop key permet d'apparier les éléments entre deux rendus.

JSX
function List({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          <input defaultValue={item.label} />
        </li>
      ))}
    </ul>
  );
}
// Avec key={index}, supprimer le premier item
// decalerait le contenu des inputs restants.
Une key stable préserve l'état et le DOM de chaque ligne.

Deuxième mécanisme fondamental : le batching. Quand plusieurs mises à jour d'état sont déclenchées dans un même contexte, React les groupe en un seul rendu. Avant React 18, ce regroupement ne fonctionnait que dans les gestionnaires d'événements React ; depuis createRoot, il est automatique partout : timers, promesses, listeners natifs. C'est aussi pourquoi lire l'état juste après un setState renvoie l'ancienne valeur : la mise à jour est planifiée, pas appliquée.

JSX
function handleTick() {
  setCount((c) => c + 1);
  setFlag((f) => !f);
  // React 18 : un seul rendu, meme ici.
}

setTimeout(handleTick, 100);
// Avant React 18, ce timer provoquait
// deux rendus successifs.
Le batching automatique couvre désormais tous les contextes.

Le rendu concurrent pousse la logique plus loin : la phase de rendu peut être interrompue, reprise, voire jetée et recommencée si une mise à jour plus urgente arrive. React classe les mises à jour par priorité (les lanes) : une frappe clavier est urgente, un filtrage de liste marqué par startTransition ne l'est pas. Conséquence directe : vos fonctions de composant doivent être pures, car React peut les appeler plusieurs fois sans jamais committer le résultat. Autre cas limite : un store externe (Redux, Zustand, un singleton maison) lu pendant un rendu interruptible peut changer en cours de rendu, produisant une interface incohérente appelée tearing. L'API useSyncExternalStore résout ce problème en forçant un rendu synchrone quand le store change.

JAVASCRIPT
import { useSyncExternalStore } from 'react';

function useWindowWidth() {
  return useSyncExternalStore(
    (notify) => {
      window.addEventListener('resize', notify);
      return () =>
        window.removeEventListener('resize', notify);
    },
    () => window.innerWidth
  );
}
Lecture d'un store externe sans risque de tearing.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Lors de la réconciliation, que fait React si un élément <div> devient un <span> à la même position ?
    • Il met à jour la balise en conservant les enfants
    • Il détruit tout le sous-arbre, état compris, et le reconstruit
    • Il compare récursivement les enfants avant de décider
    • Il déclenche une erreur de réconciliation
  2. Avec React 18 et createRoot, deux appels à setState dans un setTimeout provoquent combien de rendus ?
    • Deux, car le batching ne s'applique qu'aux événements React
    • Un seul, grâce au batching automatique
    • Zéro, les mises à jour hors événement sont ignorées
  3. Quelle phase du travail de React est interruptible en mode concurrent ?
    • La phase de commit uniquement
    • La phase de rendu uniquement
    • Les deux phases
    • Aucune, React reste entièrement synchrone