Comment utiliser les Hooks React : Guide complet avec exemples
Comment utiliser les Hooks React : Guide complet avec exemples
Quand React 16.8 a introduit les Hooks début 2019, cela a fondamentalement changé la façon dont les développeurs construisent des applications React en permettant aux composants fonctionnels de gérer l'état, les effets de bord et le contexte sans écrire de composants de classe. Comprendre comment utiliser les Hooks React est désormais essentiel pour tout développeur React moderne, car les Hooks sont devenus « la seule façon dont la plupart du code React est écrit ». Ce guide vous présentera les Hooks intégrés essentiels, expliquera les règles qui les régissent et fournira des exemples pratiques que vous pourrez utiliser immédiatement.
Ce que vous allez apprendre
À la fin de ce guide, vous utiliserez avec confiance les Hooks principaux de React — useState, useEffect, useContext, useRef, useReducer, useMemo et useCallback — dans vos propres projets. Vous comprendrez quand choisir un Hook plutôt qu'un autre, comment éviter les pièges courants comme les rendus inutiles, et comment extraire la logique réutilisable dans des Hooks personnalisés. Le point le plus important à retenir est que les Hooks ne sont que des fonctions — apprendre à les composer est la clé pour maîtriser React.
Comprendre les règles des Hooks
Avant de plonger dans les Hooks individuels, vous devez comprendre deux règles non négociables appliquées par le paquet eslint-plugin-react-hooks :
Appelez les Hooks uniquement au niveau supérieur. N'appelez jamais de Hooks à l'intérieur de boucles, de conditions ou de fonctions imbriquées. React se base sur l'ordre dans lequel les Hooks sont appelés pour préserver l'état entre les rendus — si l'ordre change, React ne peut pas associer correctement l'état à son Hook.
Appelez les Hooks uniquement depuis des fonctions React. Vous ne pouvez appeler des Hooks que depuis des composants fonctionnels ou depuis d'autres Hooks personnalisés. Une fonction JavaScript ordinaire ne peut pas appeler
useStateouuseEffect.Google AdInline article slot
Une convention découle de la deuxième règle : toute fonction qui appelle des Hooks doit commencer par use (par exemple, useCustomHook). Le linter utilise ce préfixe pour appliquer correctement les règles.
Les Hooks les plus essentiels que vous utiliserez quotidiennement
useState : Gérer l'état d'un composant
Le Hook useState est le Hook le plus fondamental, vous permettant d'ajouter un état aux composants fonctionnels. Il retourne un tableau avec deux éléments : la valeur actuelle de l'état et une fonction pour la mettre à jour.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // L'état initial est 0
return (
<button onClick={() => setCount(count + 1)}>
Cliqué {count} fois
</button>
);
}
Lors de la mise à jour de l'état basée sur la valeur précédente, utilisez la forme de mise à jour fonctionnelle pour éviter les conditions de concurrence, surtout avec le regroupement automatique de React :
// Sûr : utilise la valeur d'état précédente
setCount(prevCount => prevCount + 1);
// Également sûr pour les objets : étend l'état précédent
setUser(prevUser => ({ ...prevUser, name: 'Jane' }));
Si le calcul de l'état initial est coûteux, passez une fonction plutôt qu'une valeur — React ne l'exécutera que lors du rendu initial :
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
useEffect : Gérer les effets de bord
Le Hook useEffect vous permet d'effectuer des effets de bord dans les composants fonctionnels, comme récupérer des données, configurer des abonnements ou modifier manuellement le DOM. Par défaut, les effets s'exécutent après chaque rendu terminé, mais vous pouvez contrôler quand ils se déclenchent en utilisant le tableau de dépendances.
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Cet effet s'exécute après le montage du composant et lorsque userId change
fetchUser(userId).then(data => setUser(data));
// Fonction de nettoyage optionnelle s'exécute avant le prochain effet et lors du démontage
return () => {
// Annulez les requêtes en attente ou nettoyez les abonnements
};
}, [userId]); // Tableau de dépendances : ne se réexécute que lorsque userId change
return <div>{user?.name}</div>;
}
Modèles clés pour useEffect :
- Tableau de dépendances vide
[]: L'effet s'exécute une fois après le rendu initial (montage). - Pas de tableau de dépendances : L'effet s'exécute après chaque rendu (rarement ce que vous voulez).
- Fonction de nettoyage : Empêche les fuites de mémoire en nettoyant les abonnements, les minuteurs ou les requêtes réseau en attente.
Avertissement important : Tous les changements d'état ne nécessitent pas un effet. Si vous pouvez calculer une valeur pendant le rendu, faites-le directement au lieu d'utiliser useEffect :
// ❌ Effet inutile
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
// ✅ Calcul direct pendant le rendu
const fullName = `${firstName} ${lastName}`;
useContext : Consommer le contexte sans prop drilling
Le Hook useContext vous permet de lire les valeurs du contexte React sans envelopper votre composant dans un composant Consumer :
import { useContext } from 'react';
// Créer un contexte
const ThemeContext = React.createContext('light');
function ThemedButton() {
// Lire la valeur du contexte directement
const theme = useContext(ThemeContext);
return <button className={theme}>Cliquez-moi</button>;
}
Lorsque la valeur du fournisseur de contexte change, tout composant utilisant useContext se rend automatiquement.
useRef : Référencer des valeurs qui ne déclenchent pas de rendus
Le Hook useRef crée un objet ref mutable dont la propriété .current persiste entre les rendus. Contrairement à l'état, la mise à jour d'une ref ne provoque pas de rendu. Cela rend les refs idéales pour :
- Stocker des références d'éléments DOM
- Garder la trace des identifiants de minuteurs
- Persister des valeurs qui ne devraient pas déclencher de mises à jour de l'interface utilisateur
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focus sur l'input après le montage du composant
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
useReducer : Gérer une logique d'état complexe
Pour un état qui implique plusieurs sous-valeurs ou des transitions complexes, useReducer est souvent préférable à useState. Il accepte une fonction réductrice et un état initial, retournant l'état actuel et une fonction de dispatch :
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Compteur : {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Réinitialiser</button>
</>
);
}
Hooks d'optimisation des performances
useMemo : Mettre en cache des calculs coûteux
useMemo met en cache le résultat d'un calcul et ne le recalcule que lorsque les dépendances changent :
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// Ne recalcule que lorsque todos ou filter changent
const filteredTodos = useMemo(() => {
return todos.filter(todo => todo.status === filter);
}, [todos, filter]);
return (
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
useCallback : Mémoiser les références de fonctions
useCallback retourne une version mémoisée d'une fonction de rappel qui ne change que si les dépendances changent. C'est particulièrement utile lors du passage de fonctions de rappel à des composants enfants optimisés qui reposent sur l'égalité de référence pour éviter des rendus inutiles :
import { useCallback } from 'react';
function ParentComponent() {
const handleClick = useCallback(() => {
console.log('Bouton cliqué');
}, []); // La référence de la fonction reste stable entre les rendus
return <ChildButton onClick={handleClick} />;
}
Hooks personnalisés : Réutiliser la logique entre les composants
La véritable puissance des Hooks émerge lorsque vous créez des Hooks personnalisés pour extraire et réutiliser la logique avec état. Un Hook personnalisé est simplement une fonction JavaScript dont le nom commence par use et qui peut appeler d'autres Hooks.
Exemple : useLocalStorage
Ce Hook personnalisé synchronise l'état avec localStorage, persistant les données entre les rechargements de page :
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
if (typeof window === 'undefined') return initialValue;
const stored = localStorage.getItem(key);
return stored !== null ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Utilisation
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Passer en mode {theme === 'light' ? 'sombre' : 'clair'}
</button>
);
}
Exemple : useMediaQuery
Ce Hook vous permet de rendre des composants de manière conditionnelle en fonction des requêtes média CSS, comme la préférence de l'utilisateur pour une animation réduite :
import { useState, useEffect } from 'react';
function useMediaQuery(query) {
const [matches, setMatches] = useState(() => {
if (typeof window === 'undefined') return false;
return window.matchMedia(query).matches;
});
useEffect(() => {
const mediaQueryList = window.matchMedia(query);
const onChange = (event) => setMatches(event.matches);
mediaQueryList.addEventListener('change', onChange);
return () => mediaQueryList.removeEventListener('change', onChange);
}, [query]);
return matches;
}
// Utilisation
function MotionAwareComponent() {
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
return prefersReducedMotion ? <StaticHero /> : <AnimatedHero />;
}
React 19 : Nouveaux Hooks pour les formulaires et les données asynchrones
React 19 (sorti en décembre 2024) a introduit plusieurs nouveaux Hooks conçus pour simplifier la gestion des formulaires et les opérations asynchrones.
useActionState : Simplifier la soumission de formulaires
useActionState gère l'état de soumission du formulaire, retournant le dernier résultat, une fonction d'action et un indicateur d'attente :
import { useActionState } from 'react';
async function subscribeAction(prevState, formData) {
const email = formData.get('email');
try {
await subscribe(email);
return { ok: true };
} catch (error) {
return { ok: false, error: error.message };
}
}
function NewsletterForm() {
const [state, formAction, isPending] = useActionState(
subscribeAction,
{ ok: false }
);
return (
<form action={formAction}>
<input name="email" type="email" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Abonnement en cours...' : "S'abonner"}
</button>
{state.ok && <p>Vous êtes abonné !</p>}
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
</form>
);
}
useFormStatus : Accéder à l'état du formulaire parent
useFormStatus permet aux composants imbriqués de savoir si leur formulaire parent est en cours de soumission :
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Enregistrement...' : 'Enregistrer'}
</button>
);
}
Intégration avec Redux : Le Hook useSelector
Lors de l'utilisation de Redux avec React, le Hook useSelector est la méthode moderne et recommandée pour accéder à l'état depuis le store Redux. Il remplace l'ancien composant d'ordre supérieur connect et fonctionne mieux avec TypeScript.
import { useSelector, useDispatch } from 'react-redux';
function CounterComponent() {
// Extraire des données spécifiques du store Redux
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>Incrémenter</button>
</div>
);
}
Important : useSelector utilise par défaut une égalité de référence stricte ===. Retourner un nouvel objet à chaque fois forcera des rendus. Utilisez des sélecteurs mémoisés ou appelez useSelector plusieurs fois pour des champs individuels afin d'optimiser les performances :
// ❌ Cela provoque des rendus à chaque action
const { count, user } = useSelector((state) => ({
count: state.count,
user: state.user
}));
// ✅ Appelez useSelector pour chaque champ
const count = useSelector((state) => state.count);
const user = useSelector((state) => state.user);
Sources
- Documentation officielle de React : Hooks React intégrés
- Documentation officielle de React : Référence de l'API des Hooks
- Documentation de React Redux : API des Hooks
- Patterns.dev : Modèle des Hooks
- Documentation officielle de React : Utilisation du Hook d'état
— Editorial Team
Aucun commentaire pour le moment.