React Hooks sont des fonctions qui permettent d’utiliser l’état, les effets, le contexte et d’autres fonctionnalités de React dans des composants fonctionnels, sans écrire de classes. Introduits dans React 16.8, les hooks ont radicalement changé l’approche de développement, remplaçant des motifs complexes comme HOC et render props. Selon React, 2024, il existe 15 hooks intégrés couvrant tous les scénarios de gestion de composants.
À retenir
React Hooks sont un ensemble de fonctions intégrées à React qui permettent de « se brancher » sur les mécanismes internes du framework sans créer de classes. Avant React 16.8, les composants fonctionnels pouvaient seulement accepter des props et retourner du JSX — toute logique complexe nécessitait un composant de classe.
Le problème des composants de classe est la division de la logique connexe. Le code lié à une même entité (par exemple, un abonnement WebSocket) était forcément divisé entre componentDidMount, componentDidUpdate et componentWillUnmount. Les hooks résolvent cela : toute la logique est groupée dans un seul useEffect.
Le deuxième problème est la réutilisation de la logique. Pour réutiliser la logique d’état entre composants de classe, il fallait appliquer des HOC (Composants d’Ordre Supérieur) ou render props — les deux motifs créaient un imbrication supplémentaire dans l’arbre de composants. Les hooks personnalisés résolvent cela sans imbrication.
useState est le hook le plus basique. Il prend une valeur initiale et retourne un tuple : la valeur actuelle et une fonction pour la mettre à jour. Contrairement à this.setState dans les classes, useState ne fusionne pas les objets — le nouvel état remplace complètement le précédent pour un appel useState spécifique.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
Le setter de useState peut accepter une fonction qui reçoit la valeur précédente et en retourne une nouvelle. La forme fonctionnelle est obligatoire lorsque le nouvel état est calculé à partir du précédent et que des appels setState groupés sont possibles — chaque appel recevra la valeur précédente actuelle.
useEffect est un hook pour effectuer des effets de bord dans les composants fonctionnels. Il prend deux arguments : une fonction d’effet et un tableau de dépendances. React exécute l’effet après chaque rendu, mais lorsque des dépendances sont spécifiées — seulement lorsque l’une d’elles change.
Un tableau de dépendances vide [] signifie que l’effet s’exécute une fois après le premier rendu (analogue à componentDidMount). Retourner une fonction depuis l’effet est un nettoyage qui s’exécute au démontage (analogue à componentWillUnmount) et avant la réexécution de l’effet.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Nettoyage - fermer le socket
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
Les dépendances de useEffect sont la seule connexion entre l’effet et le monde extérieur. Si l’effet utilise une variable des props ou de l’état, elle doit figurer dans le tableau de dépendances. Ignorer cette règle est une source de bogues avec des fermetures périmées (stale closures).
useContext est un hook pour lire les valeurs du contexte. Il prend un objet contexte créé par React.createContext et retourne la valeur actuelle. Si le contexte change, le composant se re-rend automatiquement avec la nouvelle valeur.
Avant useContext, accéder au contexte dans les composants fonctionnels nécessitait un enveloppement dans Context.Consumer, ce qui créait de l’imbrication. useContext élimine complètement Consumer — il suffit d’appeler le hook n’importe où dans le composant fonctionnel.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('clair');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext n’est pas un remplacement de Redux ou de l’état global. React recommande d’utiliser le contexte pour des valeurs qui changent rarement — thème, locale, statut d’authentification. Pour des données fréquemment mises à jour, chaque re-rendu recalcule tous les consommateurs du contexte, ce qui peut réduire les performances.
useReducer est une alternative à useState pour les états avec une logique de mise à jour complexe. Il prend une fonction réductrice et un état initial, retourne l’état actuel et une fonction dispatch. Le réducteur reçoit l’état actuel et une action, retourne un nouvel état — une fonction pure sans effets de bord.
useReducer est particulièrement utile lorsque plusieurs actions (set, reset, increment, addItem) gèrent un même état. Au lieu de plusieurs useState avec différents setters — un seul réducteur avec des types d’action clairs. Cela rend la logique prévisible et facile à tester.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef crée un objet mutable qui persiste entre les rendus. Modifier current ne déclenche pas un re-rendu. Les principales utilisations sont l’accès aux éléments DOM et le stockage de valeurs mutables (minuteurs, valeurs précédentes, instances de bibliothèques).
useMemo mémorise le résultat d’une fonction. Il ne recalcule la valeur que lorsqu’une dépendance change. Utilisez-le pour des calculs coûteux — filtrage de grands tableaux, tri, formatage de données. useCallback est un cas particulier de useMemo pour mémoriser des fonctions.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofocus au montage
useEffect(() => {
inputRef.current?.focus();
}, []);
// Mémoriser le résultat filtré
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Mémoriser le callback
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Une règle importante : useRef, useMemo et useCallback sont des outils d’optimisation, pas des constructions obligatoires. N’enveloppez pas tout dans useMemo — mesurez d’abord les performances. L’optimisation prématurée complique le code. React rend les composants rapidement ; useMemo n’est nécessaire que lorsque les calculs sont vraiment coûteux (O(n²) et plus).
React a introduit deux règles strictes pour les hooks. Première : appelez les hooks uniquement au niveau supérieur — pas à l’intérieur de conditions, boucles ou fonctions imbriquées. Deuxième : appelez les hooks uniquement à partir de composants fonctionnels React ou de hooks personnalisés. Le plugin ESLint eslint-plugin-react-hooks vérifie automatiquement les deux règles.
L’erreur la plus courante est la violation de l’ordre d’appel des hooks. React repose sur l’ordre d’appel des hooks entre les rendus. Si un hook se trouve à l’intérieur d’une condition et ne s’exécute pas lors d’un rendu, tous les hooks suivants se décalent, provoquant des bogues.
| Erreur | Ce qui se passe | Solution |
|---|---|---|
| Hook dans une condition | L’ordre des hooks est brisé | Déplacez la logique conditionnelle à l’intérieur du hook |
| Stale closure | L’effet utilise une valeur ancienne | Ajoutez toutes les dépendances au tableau |
| Boucle infinie | useEffect sans dépendances modifie l’état | Spécifiez les dépendances ou utilisez useReducer |
| Mutation de ref dans le rendu | Effet de bord dans le corps de la fonction | Déplacez les mutations dans useEffect |
La deuxième erreur courante est la stale closure dans useEffect. Si l’effet utilise une variable non spécifiée dans les dépendances, l’effet «capture» sa valeur au moment de la création de la fermeture. La solution est de toujours inclure toutes les variables utilisées dans le tableau de dépendances. S’il y a trop de dépendances, envisagez de diviser l’effet en plusieurs.
Foire aux questions
React repose sur l’ordre d’appel des hooks entre les rendus. Si un hook est appelé à l’intérieur d’une condition et ne se déclenche pas lors d’un rendu, tous les hooks suivants se décalent — leur état se mélange. La règle ESLint hooks/exhaustive-deps avertit de cela.
useLayoutEffect est synchrone et s’exécute avant que le navigateur n’affiche les modifications. useEffect est asynchrone — après l’affichage. Utilisez useLayoutEffect uniquement lorsque vous devez mesurer ou modifier le DOM avant que l’utilisateur ne voie les changements (position d’une popover, restauration du défilement).
Utilisez AbortController à l’intérieur de useEffect. Créez un controller, passez le signal à fetch, et appelez controller.abort() dans la fonction de nettoyage. Pour les navigateurs plus anciens, utilisez un indicateur cancelled : return () => { cancelled = true; } avec une vérification avant setState.
Un hook personnalisé est une fonction JavaScript avec le préfixe use qui utilise des hooks intégrés. Créez-en un lorsque la même logique avec des hooks se répète dans deux composants ou plus. Exemples : useDebounce, useLocalStorage, useMediaQuery. Les hooks personnalisés remplacent les HOC et render props.
En Strict Mode, React monte et démonte intentionnellement le composant deux fois en développement pour trouver des bogues avec des effets non nettoyés. Si le nettoyage est écrit correctement, le double appel ne pose aucun problème. Cela ne se produit pas en mode production.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi