React - Rendu et Virtual DOM

React suit une approche déclarative pour le rendu des composants, ce qui signifie que tu spécifies ce à quoi un composant doit ressembler, et React s'occupe de l'afficher à l'écran. C'est l'opposé d'une approche impérative, où tu écrirais du code pour manipuler manuellement le DOM.


Le Processus de Rendu React

Les 3 étapes fondamentales

React fonctionne comme un serveur dans un restaurant :

  1. Trigger (Déclenchement) - Le client passe commande
  2. Render (Rendu) - Le chef prépare le plat
  3. Commit (Validation) - Le serveur apporte le plat
function ExempleRendu() {
  const [count, setCount] = useState(0)

  console.log('RENDER: Composant en cours de rendu')

  const handleClick = () => {
    console.log('🎯 TRIGGER: Changement d\'état déclenché')
    setCount(count + 1) // ← Déclenche un nouveau rendu
  }

  return (
    <div>
      <p>Compteur: {count}</p>
      <button onClick={handleClick}>+1</button>
    </div>
  )
}

1. Trigger - Quand React décide de faire un rendu

React fait un rendu dans 2 cas seulement :

function QuandReactRend() {
  const [state, setState] = useState(0)

  // ✅ CAS 1: Rendu initial (première fois)
  useEffect(() => {
    console.log('🎬 Premier rendu du composant')
  }, [])

  // ✅ CAS 2: Changement d'état
  const triggerReRender = () => {
    setState(state + 1) // ← Déclenche un re-rendu
  }

  return (
    <div>
      <p>State: {state}</p>
      <button onClick={triggerReRender}>Changer état</button>
    </div>
  )
}

2. Render - React appelle tes composants

function ExempleRenderPur() {
  const [count, setCount] = useState(0)

  // ⚠️ Cette fonction DOIT être pure !
  // Mêmes inputs = mêmes outputs
  console.log('🔄 Render appelé avec count =', count)

  return <div>Count: {count}</div>
}

// ❌ MAUVAIS - Effet de bord pendant le render
function BadExample() {
  const [count, setCount] = useState(0)

  // ❌ Ne jamais faire ça dans le render !
  document.title = `Count: ${count}` // Effet de bord
  Math.random() // Non-déterministe

  return <div>{count}</div>
}

// ✅ BON - Render pur
function GoodExample() {
  const [count, setCount] = useState(0)

  // ✅ Effets de bord dans useEffect
  useEffect(() => {
    document.title = `Count: ${count}`
  }, [count])

  return <div>{count}</div>
}

3. Commit - React met à jour le DOM

function ExempleCommit() {
  const [color, setColor] = useState('red')

  return (
    <div>
      {/* React ne mettra à jour QUE la couleur dans le DOM */}
      <div style={{ backgroundColor: color, padding: '20px' }}>
        <h2>Titre inchangé</h2>
        <p>Paragraphe inchangé</p>
        <button onClick={() => setColor(color === 'red' ? 'blue' : 'red')}>
          Changer couleur
        </button>
      </div>
    </div>
  )
}

Virtual DOM - La Magie de React

Qu'est-ce que le Virtual DOM ?

Le Virtual DOM (VDOM) est une représentation en mémoire du DOM réel. C'est comme un brouillon que React utilise pour optimiser les mises à jour.

// Ce que tu écris en JSX
function MonComposant() {
  return (
    <div className="container">
      <h1>Titre</h1>
      <p>Paragraphe</p>
    </div>
  )
}

// Ce que React crée en Virtual DOM (simplifié)
const virtualDOM = {
  type: 'div',
  props: { className: 'container' },
  children: [
    { type: 'h1', props: {}, children: ['Titre'] },
    { type: 'p', props: {}, children: ['Paragraphe'] }
  ]
}

Le Processus de Réconciliation

function ExempleReconciliation() {
  const [items, setItems] = useState(['A', 'B', 'C'])

  const ajouterItem = () => {
    setItems([...items, `Item ${Date.now()}`])
  }

  return (
    <div>
      <h2>Liste d'items</h2>
      {/*
        React compare :
        - AVANT: ['A', 'B', 'C']
        - APRÈS: ['A', 'B', 'C', 'Item 12345']

        Résultat: React ajoute SEULEMENT le nouvel élément au DOM
      */}
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={ajouterItem}>Ajouter item</button>
    </div>
  )
}

Pourquoi c'est important ?

// Sans Virtual DOM (manipulation directe)
function updateWithoutVDOM() {
  // ❌ Lent et inefficace
  document.getElementById('title').textContent = 'Nouveau titre'
  document.getElementById('count').textContent = '42'
  document.getElementById('status').className = 'active'
  // ... 50 autres mises à jour
}

// Avec Virtual DOM (React)
function updateWithVDOM() {
  // ✅ React calcule automatiquement les changements minimaux
  setTitle('Nouveau titre')
  setCount(42)
  setStatus('active')
  // React met à jour SEULEMENT ce qui a changé
}

Démonstration pratique

function DemoVirtualDOM() {
  const [highlightIndex, setHighlightIndex] = useState(0)

  useEffect(() => {
    const interval = setInterval(() => {
      setHighlightIndex(prev => (prev + 1) % 5)
    }, 1000)

    return () => clearInterval(interval)
  }, [])

  return (
    <div>
      <h3>React met à jour SEULEMENT l'élément surligné</h3>
      <ul>
        {['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'].map((item, index) => (
          <li
            key={index}
            style={{
              backgroundColor: index === highlightIndex ? 'yellow' : 'transparent',
              padding: '10px',
              transition: 'background-color 0.3s'
            }}
          >
            {item}
          </li>
        ))}
      </ul>
      <p>👆 Ouvre les DevTools et regarde: seule la couleur change dans le DOM !</p>
    </div>
  )
}

Ressources pour aller plus loin