codelynx.dev
🇫🇷🇬🇧

Retour 28/11/2024

Apprendre tous les hooks en React

Écris par Melvyn Malherbe le 28/11/2024


Tu souhaites apprendre tous les hooks en React ? Ou plutôt tous les voir pour mieux comprendre comment ils fonctionnent ? Dans cet article, je vais t'expliquer chaque hook React avec un exemple concret pour m'assurer que tu les comprennes !

useState

Le hook useState te permet de stocker le state d'un composant. Le state est particulier car chaque fois qu'il change, il provoque un "re-render" qui signifie "le composant est recalculé par React pour connaître le nouveau rendu" afin de pouvoir afficher la nouvelle valeur.

On l'utilise comme ça :

TSX
//    👇 getter  👇 setter         👇 valeur initiale
const [count, setCount] = useState(0);

Voici un exemple concret :

Ce qu'il se passe quand le state change :

  1. Le composant est re-render
  2. React récupère le JSX qui est retourné
  3. Il regarde si des éléments ont changé
  4. Si oui, il les met à jour
  5. Si non, il ne fait rien

Optimisation avec un initializer

Si ta valeur initiale est coûteuse à calculer (comme un gros tableau ou un calcul complexe), tu peux passer une fonction d'initialisation à useState :

TSX
// ❌ createInitialTodos est appelé à chaque render
const [todos, setTodos] = useState(createInitialTodos());

// ✅ createInitialTodos est appelé uniquement à l'initialisation
const [todos, setTodos] = useState(() => createInitialTodos());

useEffect

Le hook useEffect te permet d'exécuter du code en réaction à des changements dans ton composant. C'est parfait pour synchroniser ton composant avec des systèmes externes comme une API ou le localStorage.

TSX
useEffect(() => {
  // Ce code s'exécute après le render
  document.title = `Vous avez cliqué ${count} fois`;
}, [count]); // Uniquement si count change

Les cas d'utilisation principaux :

  1. Synchronisation avec des APIs externes
  2. Souscription à des événements
  3. Mise à jour du DOM manuellement
  4. Logging
  5. Chargement de données

Les pièges à éviter

  1. Ne pas mettre de dépendances dans le tableau quand il en faut
  2. Mettre trop de dépendances et créer des boucles infinies
  3. Oublier de nettoyer (cleanup) les effets qui en ont besoin

Voici un exemple correct avec cleanup :

TSX
useEffect(() => {
  const subscription = api.subscribe(data => {
    // Faire quelque chose avec data
  });
  
  // Cleanup : se désabonner quand le composant est démonté
  return () => {
    subscription.unsubscribe();
  };
}, []);

useContext

Le hook useContext te permet d'accéder à des valeurs partagées dans ton arbre de composants sans avoir à passer les props manuellement à chaque niveau.

TSX
// 1. Créer le contexte
const ThemeContext = createContext('light');

// 2. Fournir une valeur
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Button />
    </ThemeContext.Provider>
  );
}

// 3. Consommer la valeur
function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Je suis {theme}</button>;
}

Voici un exemple :

useRef

Le hook useRef te permet de garder une référence mutable qui persiste entre les renders. C'est très utile pour :

  1. Stocker des références DOM
  2. Garder des valeurs qui ne déclenchent pas de re-render
  3. Stocker des valeurs précédentes
TSX
function TextInputWithFocusButton() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus l'input</button>
    </>
  );
}

Exemple complet :

useMemo et useCallback

Ces hooks sont utilisés pour l'optimisation des performances :

  • useMemo mémorise le résultat d'un calcul coûteux
  • useCallback mémorise une fonction
TSX
// Mémoiser un calcul coûteux
const memoizedValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

// Mémoiser une fonction
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

Exemple complet :

useReducer

Pour des states plus complexes, useReducer te permet de gérer les mises à jour de state avec un pattern similaire à Redux :

TSX
const [state, dispatch] = useReducer(reducer, initialState);

// Dans ton reducer
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

Exemple complet :

useActionState

Le hook useActionState est un nouveau hook disponible dans la version Canary de React qui te permet de gérer l'état d'un formulaire en fonction du résultat d'une action.

TSX
const [state, formAction, isPending] = useActionState(fn, initialState);

C'est particulièrement utile pour :

  1. Gérer les états de formulaire
  2. Afficher des messages d'erreur
  3. Mettre à jour l'UI en fonction du résultat d'une action

Exemple concret :

TSX
import { useActionState } from "react";

async function increment(previousState, formData) {
  return previousState + 1;
}

function Counter() {
  const [count, formAction] = useActionState(increment, 0);
  
  return (
    <form>
      <p>Compteur: {count}</p>
      <button formAction={formAction}>Incrémenter</button>
    </form>
  );
}

useOptimistic

Le hook useOptimistic est un nouveau hook qui te permet d'afficher un état optimiste pendant qu'une action est en cours. C'est super utile pour améliorer l'expérience utilisateur en montrant immédiatement le résultat attendu d'une action.

TSX
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);

Voici un exemple avec un bouton "J'aime" :

useInsertionEffect

Le hook useInsertionEffect est similaire à useEffect, mais il s'exécute de manière synchrone avant toutes les mutations DOM. Il est principalement destiné aux bibliothèques CSS-in-JS.

TSX
useInsertionEffect(setup, dependencies?);

⚠️ Attention

  • Ce hook est destiné aux bibliothèques CSS-in-JS, pas à l'utilisation dans des applications normales
  • Il s'exécute uniquement côté client
  • Il n'a pas accès aux refs
TSX
// Exemple d'utilisation dans une bibliothèque CSS-in-JS
useInsertionEffect(() => {
  if (!isInserted.current) {
    isInserted.current = true;
    // Injecter des styles CSS dynamiques ici
  }
});

useSyncExternalStore

Le hook useSyncExternalStore te permet de souscrire à une source de données externe de manière sûre pour la concurrence.

TSX
const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?);

C'est particulièrement utile pour :

  1. S'abonner à des stores externes (Redux, MobX, etc.)
  2. Gérer des événements du navigateur
  3. Synchroniser avec des APIs externes

Exemple avec le statut en ligne/hors ligne :

Ces nouveaux hooks apportent des fonctionnalités puissantes pour gérer des cas d'usage spécifiques comme l'optimistic UI, la gestion de formulaires serveur et l'intégration avec des systèmes externes. N'hésite pas à les explorer pour améliorer l'expérience utilisateur de tes applications !

Conclusion

Les hooks sont la base de la gestion d'état et des effets de bord dans React. En comprenant bien chacun d'entre eux, tu pourras :

  • Créer des composants plus maintenables
  • Optimiser les performances quand nécessaire
  • Partager de la logique entre composants
  • Gérer proprement les effets de bord

N'hésite pas à pratiquer avec chaque hook individuellement avant de les combiner. La pratique est la clé pour bien les maîtriser !

BeginReact

Cours React gratuit

Accède à des exercices, des vidéos et bien plus sur React dans la formation "BeginReact" 👇