useEffect()

Dans React, lorsqu’on souhaite récupérer des données externes (par exemple depuis une API), on ne doit pas le faire directement dans le corps du component, car celui-ci peut être réexécuté plusieurs fois.

La bonne pratique consiste à utiliser le hook useEffect, qui permet d’exécuter une action après le rendu initial du composant (et à chaque mise à jour si on le souhaite).

Exemple inadéquat : appel direct dans le component

fetch("https://pokeapi.co/api/v2/pokemon/pikachu")
  .then(res => res.json())
  .then(data => console.log(data)) // déclenché à chaque render !

Le hook useEffect permet d’exécuter une fonction après le rendu du composant, et d’en contrôler la fréquence d’exécution.

Voici comment faire le même appel de manière propre, avec useEffect :

import { useState, useEffect } from "react"

export default function App() {
  const [pokemonData, setPokemonData] = useState(null)

  useEffect(() => {
    fetch("https://pokeapi.co/api/v2/pokemon/pikachu")
      .then(res => res.json())
      .then(data => setPokemonData(data))
  }, [])

  return (
    <div>
      <pre>{JSON.stringify(pokemonData, null, 2)}</pre>
    </div>
  )
}

La syntaxe de useEffect

React.useEffect(() => {
  // code exécuté après le rendu
}, [/* dépendances */])
  • Le premier argument est une fonction callback appelée après le rendu.
  • Le deuxième argument est un tableau de dépendances :
    • [] → l’effet est exécuté une seule fois (au montage du composant)
    • [count] → l’effet s’exécute à chaque fois que count change

Exemple : démonstration avec un compteur

Ce composant affiche un bouton pour incrémenter un compteur. Un appel API est placé dans useEffect sans tableau de dépendances, ce qui provoque un appel à chaque re-render :

import React from "react"

export default function App() {
  const [starWarsData, setStarWarsData] = React.useState({})
  const [count, setCount] = React.useState(0)

  console.log("Rendered!")

  React.useEffect(() => {
    console.log("Effect ran")
    fetch("https://swapi.dev/api/people/1")
      .then(res => res.json())
    // .then(data => setStarWarsData(data))
  })

  return (
    <div>
      <h2>The count is {count}</h2>
      <button onClick={() => setCount(prev => prev + 1)}>Add</button>
      <pre>{JSON.stringify(starWarsData, null, 2)}</pre>
    </div>
  )
}

Comprendre les dépendances

React compare le tableau de dépendances entre deux rendus. Si une valeur a changé, l’effet est relancé.

  • [] → effet exécuté une seule fois
  • [count] → effet exécuté à chaque changement de count

Dans l’exemple précédent, comme count n’est pas utilisé dans l’effet, on peut le retirer du tableau (ou même ne pas en mettre).

Exercice — afficher les pokémon un à un

Voici un composant qui utilise count pour charger dynamiquement un Pokémon différent depuis l’API :

import React from "react"

export default function App() {
  const [pokemonData, setPokemonData] = React.useState({})
  const [count, setCount] = React.useState(1)

  React.useEffect(() => {
    fetch(`https://pokeapi.co/api/v2/pokemon/${count}`)
      .then(res => res.json())
      .then(data => setPokemonData(data))
  }, [count])

  return (
    <div>
      <h2>Le numéro est {count}</h2>
      <button onClick={() => setCount(prev => prev + 1)}>Afficher le prochain Pokémon</button>
      <pre>{JSON.stringify(pokemonData, null, 2)}</pre>
    </div>
  )
}

Objectif : comprendre comment useEffect se réexécute à chaque changement de count, ce qui permet d’afficher un pokémon différent à chaque clic.

Il est possible d’aller plus loin en :

  • affichant le nom (pokemonData.name) et l’image (pokemonData.sprites.front_default),
  • ajoutant un champ de saisie pour chercher un pokémon par son nom.