Les hooks React sont des fonctions comme useState, useEffect et useRef qui donnent aux composants fonctions un état, des effets de bord et des refs. Cet aide-mémoire couvre chaque hook intégré de React 19.3 avec son rôle en une ligne, un exemple interactif et son erreur la plus fréquente, plus 12 hooks personnalisés à copier.
Au moment où nous écrivons ces lignes (octobre 2026), la dernière version de React est la 19.3. Le tableau liste tous les hooks de la référence officielle des hooks React, plus les trois hooks React 19 documentés sur leurs propres pages (use, useOptimistic et le hook React DOM useFormStatus). Les hooks marqués React 18 fonctionnent dans React 18 et 19. Les hooks marqués React 19 n'existent pas dans React 18. Chaque hook React 18 ci-dessous a un exemple interactif sur lequel vous pouvez cliquer ; ceux de React 19 sont présentés uniquement sous forme de code, car cette page tourne sur React 18.
| Hook | Groupe | Sert à | Depuis |
|---|---|---|---|
| useState | État | conserver une valeur qui refait le rendu du composant quand elle change | React 18 |
| useReducer | État | gérer un état aux nombreuses mises à jour liées via une seule fonction reducer | React 18 |
| useContext | Contexte | lire une valeur depuis le provider le plus proche au-dessus | React 18 |
| useRef | Ref | garder une valeur mutable ou un nœud DOM qui ne déclenche pas de rendu | React 18 |
| useImperativeHandle | Ref | choisir les méthodes qu'un parent peut appeler via une ref | React 18 |
| useEffect | Effet | se synchroniser avec quelque chose hors de React après l'affichage | React 18 |
| useLayoutEffect | Effet | mesurer la mise en page avant que le navigateur n'affiche | React 18 |
| useInsertionEffect | Effet | insérer des règles de style avant les effets de layout (bibliothèques CSS-in-JS) | React 18 |
| useEffectEvent | Effet | lire les dernières props dans un effet sans le relancer | React 19.2 |
| useMemo | Performance | mettre en cache un calcul coûteux entre les rendus | React 18 |
| useCallback | Performance | mettre en cache une fonction pour que les enfants mémoïsés ne refassent pas leur rendu | React 18 |
| useTransition | Performance | marquer une mise à jour d'état comme non urgente et obtenir un indicateur d'attente | React 18 |
| useDeferredValue | Performance | laisser une partie lente de l'interface prendre du retard sur une saisie rapide | React 18 |
| useId | Autres | générer des ID uniques et stables pour les labels et les attributs ARIA | React 18 |
| useSyncExternalStore | Autres | s'abonner à un store qui vit hors de React | React 18 |
| useDebugValue | Autres | étiqueter un hook personnalisé dans React DevTools | React 18 |
| useActionState | Autres | suivre le résultat et l'état d'attente d'une action de formulaire | React 19 |
| useOptimistic | Autres | afficher le résultat attendu d'une action avant qu'elle se termine | React 19 |
| use | API | lire une promise ou un contexte, même dans des conditions | React 19 |
| useFormStatus | React DOM | lire l'état d'attente du formulaire parent | React 19 |
Les hooks suivent deux règles : appelez-les uniquement au niveau supérieur d'un composant ou d'un hook personnalisé (jamais dans des boucles, des conditions ou des fonctions imbriquées), et appelez-les uniquement depuis des composants fonctions React ou d'autres hooks. use est la seule exception à la première règle : il peut être appelé dans des blocs if et des boucles. Le package eslint-plugin-react-hooks vérifie les deux règles pour vous.
useStateCe qu'il fait : Stocke une valeur pour ce composant. Appeler le setter planifie un nouveau rendu avec la nouvelle valeur.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((c) => c - 1)}>-1</button>
<span>{count}</span>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
</>
);
}
Erreur fréquente : Écrire setCount(count + 1) trois fois dans un même clic en espérant +3. Les trois lisent le même count de ce rendu, vous obtenez donc +1. Passez une fonction de mise à jour, setCount(c => c + 1), quand la valeur suivante dépend de la précédente. Le bouton « +3 (updater) » ci-dessus fait exactement cela. Deuxième classique : modifier un objet ou un tableau sur place (user.name = "x"; setUser(user)). React compare par référence, voit le même objet et saute le rendu. Créez plutôt un nouvel objet : setUser({ ...user, name: "x" }).
useReducerCe qu'il fait : Déplace la logique de mise à jour dans une seule fonction pure qui reçoit l'état actuel et une action, et renvoie l'état suivant. Utile quand plusieurs valeurs changent ensemble.
type Action = { type: "add"; text: string } | { type: "toggle"; id: number };
function reducer(state: Todo[], action: Action): Todo[] {
switch (action.type) {
case "add":
return [...state, { id: Date.now(), text: action.text, done: false }];
case "toggle":
return state.map((t) => (t.id === action.id ? { ...t, done: !t.done } : t));
}
}
const [todos, dispatch] = useReducer(reducer, []);
dispatch({ type: "add", text: "Ship it" });
Erreur fréquente : Faire des effets de bord dans le reducer (requêtes réseau, écriture dans localStorage, génération d'ID aléatoires à partir d'un timer dont vous dépendez ensuite). En développement, le Strict Mode de React appelle les reducers deux fois pour révéler le code impur. Gardez le reducer pur et placez les effets de bord dans des gestionnaires d'événements ou des effets.
useContextCe qu'il fait : Lit la valeur du provider correspondant le plus proche au-dessus de ce composant, et refait le rendu quand cette valeur change.
const ThemeContext = createContext<"light" | "dark">("light");
function App() {
const [theme, setTheme] = useState<"light" | "dark">("light");
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Badge() {
const theme = useContext(ThemeContext);
return <span className={theme}>Current theme: {theme}</span>;
}
Erreur fréquente : Passer un nouvel objet littéral comme valeur du provider à chaque rendu, par exemple value={{ user, setUser }}. Chaque rendu crée un nouvel objet, donc chaque consommateur refait son rendu même si rien n'a changé. Enveloppez la valeur dans useMemo, ou séparez les valeurs qui changent rarement de celles qui changent souvent dans deux contextes. Vérifiez aussi que le provider se trouve au-dessus du composant : appeler useContext dans le composant même qui rend le provider renvoie la valeur par défaut.
useRefCe qu'il fait : Vous donne une boîte (ref.current) qui survit aux rendus. La modifier ne provoque pas de rendu. Utilisez-la pour les nœuds DOM, les ID de timer et les autres valeurs que l'écran n'affiche pas.
const inputRef = useRef<HTMLInputElement>(null);
const clicks = useRef(0);
<input ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus</button>
<button onClick={() => { clicks.current += 1; }}>Count silently</button>
Erreur fréquente : Afficher ref.current dans le JSX en s'attendant à ce qu'il se mette à jour. Les refs ne déclenchent pas de rendu, donc l'écran ne se met à jour que lorsqu'autre chose refait un rendu (essayez le bouton « Ajouter à la ref », puis « Refaire le rendu » ci-dessus). Si la valeur doit apparaître à l'écran, elle doit être dans l'état. Évitez aussi de lire ou d'écrire des refs pendant le rendu, sauf pour une initialisation paresseuse unique.
useImperativeHandleCe qu'il fait : Personnalise ce que reçoit un parent quand il place une ref sur votre composant, pour exposer quelques méthodes nommées au lieu du nœud DOM entier.
type Handle = { shake: () => void; clear: () => void };
// React 18: wrap in forwardRef. In React 19 you can accept ref as a normal prop.
const FancyInput = forwardRef<Handle, Props>(function FancyInput(props, ref) {
const el = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
shake() { /* animate */ el.current?.focus(); },
clear() { if (el.current) el.current.value = ""; },
}), []);
return <input ref={el} />;
});
// parent
const ref = useRef<Handle>(null);
<FancyInput ref={ref} />
<button onClick={() => ref.current?.shake()}>Shake</button>
Erreur fréquente : L'utiliser pour piloter ce que des props peuvent exprimer. Si un parent appelle ref.current.open() pour afficher une modale, une prop isOpen est généralement plus simple et plus facile à suivre. Réservez les handles impératifs au focus, au défilement, à la lecture/pause et aux animations. Oublier le tableau de dépendances (le troisième argument) recrée aussi le handle à chaque rendu.
useEffectCe qu'il fait : Exécute du code après que React a mis à jour l'écran, pour garder un composant synchronisé avec quelque chose hors de React : un timer, un abonnement, une requête réseau, un widget non React. Renvoyez une fonction de nettoyage pour l'annuler.
useEffect(() => {
if (!running) return;
const id = setInterval(() => setSeconds((s) => s + 1), 1000);
return () => clearInterval(id); // runs before the next effect and on unmount
}, [running]);
Erreur fréquente : Utiliser un effet pour calculer quelque chose que vous pourriez calculer pendant le rendu, par exemple useEffect(() => setFullName(first + " " + last), [first, last]). Cela coûte un rendu supplémentaire et peut afficher des valeurs périmées pendant une frame. Écrivez plutôt const fullName = first + " " + last. Le deuxième bug le plus courant est un nettoyage manquant : en développement, le Strict Mode de React monte, démonte et remonte chaque composant une fois, donc un effet sans nettoyage s'abonnera ou démarrera un timer deux fois. Cette double exécution est un indice, pas un bug de React.
useLayoutEffectCe qu'il fait : Même forme que useEffect, mais s'exécute après que React a modifié le DOM et avant que le navigateur n'affiche. Utilisez-le pour mesurer un élément et ajuster la mise en page afin que l'utilisateur ne voie jamais la mauvaise position.
const boxRef = useRef<HTMLSpanElement>(null);
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
setWidth(boxRef.current!.getBoundingClientRect().width);
}, [text]);
Erreur fréquente : L'utiliser par défaut. Il bloque l'affichage, donc un travail lent à l'intérieur donne l'impression que la page est figée. Utilisez useEffect sauf si vous voyez un scintillement que seule une mesure avant affichage corrige (infobulles, positionnement de popover, dimensionnement automatique). Il ne fait rien non plus pendant le rendu serveur, ce qui produit un avertissement dans certaines configurations.
useInsertionEffectCe qu'il fait : S'exécute avant tous les effets de layout pour qu'une bibliothèque puisse insérer des règles <style> avant que quoi que ce soit ne lise la mise en page. Il existe pour les auteurs de bibliothèques CSS-in-JS.
function useScopedRule(selector: string, rule: string) {
useInsertionEffect(() => {
const style = document.createElement("style");
style.textContent = `${selector} { ${rule} }`;
document.head.appendChild(style);
return () => style.remove();
}, [selector, rule]);
}
Erreur fréquente : L'utiliser dans du code applicatif, ou essayer de mettre à jour l'état depuis celui-ci. Vous ne pouvez pas y planifier de mises à jour et les refs ne sont pas encore attachées. Si vous n'écrivez pas une bibliothèque de styles, vous voulez presque certainement useEffect ou useLayoutEffect.
useEffectEventCe qu'il fait : React 19.2 et versions ultérieures. Enveloppe une fonction pour qu'un effet puisse l'appeler et toujours voir les dernières props et le dernier état, sans lister ces valeurs comme dépendances. Utilisez-le pour la partie « événement » d'un effet, par exemple journaliser ou afficher une notification avec le thème actuel.
// React 19.2+ only. Not run on this page.
import { useEffect, useEffectEvent } from "react";
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification("Connected", theme); // always the latest theme
});
useEffect(() => {
const conn = createConnection(roomId);
conn.on("connected", () => onConnected());
conn.connect();
return () => conn.disconnect();
}, [roomId]); // theme is not a dependency, and onConnected must not be listed
}
Erreur fréquente : Mettre l'effect event dans le tableau de dépendances, l'appeler pendant le rendu ou le passer à d'autres composants. La documentation de React indique de n'appeler les effect events que depuis l'intérieur des effets (ou d'autres effect events), et sa règle de lint avertit quand vous en listez un comme dépendance. Sur React 18, le substitut habituel est le pattern « latest ref » utilisé par useInterval plus bas sur cette page.
useMemoCe qu'il fait : Met en cache le résultat d'un calcul et ne le recalcule que lorsque ses dépendances changent.
const filtered = useMemo(
() => items.filter((item) => item.includes(query)),
[items, query],
);
Erreur fréquente : Tout envelopper dedans. Mémoïser un travail peu coûteux comme a + b ou un petit map ajoute du code et un coût de comparaison sans aucun gain. Mesurez d'abord (Profiler de React DevTools) et mémoïsez les calculs lents, les valeurs passées à des enfants memo et les valeurs utilisées comme dépendances d'effet. Piège voisin : une dépendance qui est un nouvel objet à chaque rendu ({ sort: "asc" }) force le memo à recalculer à chaque fois.
useCallbackCe qu'il fait : Renvoie la même instance de fonction entre les rendus jusqu'à ce qu'une dépendance change. C'est utile quand la fonction est passée à un enfant enveloppé dans memo, ou utilisée comme dépendance d'effet.
const handleAdd = useCallback(() => setCount((c) => c + 1), []);
const Child = memo(function Child({ onClick }: { onClick: () => void }) {
return <button onClick={onClick}>Add</button>;
});
<Child onClick={handleAdd} />
Erreur fréquente : Utiliser useCallback quand l'enfant n'est pas mémoïsé. Sans memo sur l'enfant, il refait son rendu avec son parent de toute façon, donc la fonction stable ne change rien. La démo interactive montre les deux enfants : seul celui qui reçoit la fonction mise en cache saute des rendus. Les closures périmées sont l'autre piège : si le callback lit count mais que la liste de dépendances est vide, il continue de voir la première valeur. Utilisez une fonction de mise à jour ou listez la dépendance.
useTransitionCe qu'il fait : Marque une mise à jour d'état comme non urgente. React garde l'écran actuel réactif pendant qu'il prépare le nouveau, et vous donne un indicateur isPending pour signaler un chargement.
const [isPending, startTransition] = useTransition();
function selectTab(next: string) {
startTransition(() => {
setTab(next);
});
}
Erreur fréquente : Envelopper l'état propre d'un champ texte dans une transition. Les champs contrôlés doivent se mettre à jour en urgence, sinon la saisie semble cassée. Gardez l'état du champ normal et déplacez la mise à jour lente et dérivée dans la transition (ou utilisez useDeferredValue). Dans React 18, la fonction passée à startTransition doit être synchrone ; React 19 accepte aussi les fonctions async.
useDeferredValueCe qu'il fait : Vous donne une copie d'une valeur autorisée à prendre du retard. Les parties rapides de l'interface utilisent la vraie valeur ; une partie lente fait son rendu avec la valeur différée et rattrape son retard quand le navigateur est inactif.
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<SlowResults query={deferredQuery} />
</div>
Erreur fréquente : Oublier d'envelopper l'enfant lent dans memo. Si l'enfant refait de toute façon son rendu avec le parent à chaque frappe, différer sa prop n'aide pas. Ce n'est pas non plus un debounce : il n'attend pas un temps fixe et ne réduit pas les requêtes réseau. Pour moins de requêtes, utilisez un debounce (voir useDebounce plus bas).
useIdCe qu'il fait : Génère un ID unique dans l'application et identique côté serveur et côté client, pour htmlFor, aria-describedby et les attributs similaires.
function Field({ label }: { label: string }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}
Erreur fréquente : L'utiliser pour créer des clés de liste. Les clés doivent venir de vos données (un ID de la base de données), pas d'un hook. Évitez aussi Math.random() pour les ID d'éléments dans les applications rendues côté serveur : le serveur et le navigateur produisent des nombres différents et l'hydratation échoue, et c'est précisément le problème que useId résout.
useSyncExternalStoreCe qu'il fait : Abonne un composant à une source de données que React ne possède pas (une API du navigateur, un store global) et lit un snapshot qui reste cohérent pendant le rendu concurrent.
function subscribe(callback: () => void) {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
}
const online = useSyncExternalStore(
subscribe,
() => navigator.onLine, // client snapshot
() => true, // server snapshot
);
Erreur fréquente : Définir subscribe en ligne dans le composant. Une nouvelle fonction à chaque rendu force React à se désabonner et à se réabonner à chaque fois. Définissez-la hors du composant ou enveloppez-la dans useCallback. La fonction de snapshot doit aussi renvoyer la même valeur quand rien n'a changé : renvoyer un nouvel objet ou tableau à chaque appel provoque une boucle de rendu infinie.
useDebugValueCe qu'il fait : Ajoute une étiquette à un hook personnalisé dans React DevTools. Cela n'a aucun effet sur ce que voient les utilisateurs.
function useOnlineStatus() {
const online = useSyncExternalStore(subscribe, () => navigator.onLine);
useDebugValue(online ? "Online" : "Offline");
return online;
}
Erreur fréquente : L'ajouter à chaque hook personnalisé ou y faire un formatage coûteux. Utilisez-le pour les hooks partagés dont l'état est difficile à lire dans DevTools, et passez un formateur en second argument si l'étiquette est coûteuse à construire : useDebugValue(date, d => d.toISOString()).
use, useActionState, useOptimistic, useFormStatus) ne sont pas exportés par React 18. Cette page tourne elle-même sur React 18, ils sont donc présentés sous forme de code à copier dans un projet React 19, sans démo interactive. useEffectEvent, présenté plus haut, nécessite React 19.2.useActionStateCe qu'il fait : Enveloppe une action de formulaire et vous donne son dernier résultat ainsi qu'un indicateur d'attente. L'action reçoit l'état précédent et le FormData soumis.
// React 19+
import { useActionState } from "react";
async function subscribe(prev: { error?: string; ok?: boolean }, formData: FormData) {
const email = String(formData.get("email") ?? "");
if (!email.includes("@")) return { error: "Enter a valid email" };
await saveEmail(email);
return { ok: true };
}
function Newsletter() {
const [state, formAction, isPending] = useActionState(subscribe, {});
return (
<form action={formAction}>
<input name="email" type="email" />
<button disabled={isPending}>{isPending ? "Saving..." : "Subscribe"}</button>
{state.error && <p role="alert">{state.error}</p>}
{state.ok && <p>Thanks, you are subscribed.</p>}
</form>
);
}
Erreur fréquente : Oublier que le premier argument de l'action est l'état précédent. Écrire async function subscribe(formData) signifie que formData est en réalité l'ancien état et que .get lève une erreur. Notez aussi qu'il s'est brièvement appelé useFormState (dans react-dom) pendant la période canary de React 19 ; la documentation actuelle utilise useActionState depuis react.
useOptimisticCe qu'il fait : Affiche une version temporaire et optimiste de l'état pendant qu'une action async s'exécute, puis revient à l'état réel quand elle se termine.
// React 19+
import { useOptimistic } from "react";
function Thread({ messages, send }: Props) {
const [optimistic, addOptimistic] = useOptimistic(
messages,
(current, text: string) => [...current, { text, sending: true }],
);
async function action(formData: FormData) {
const text = String(formData.get("text"));
addOptimistic(text);
await send(text); // parent updates `messages` when this resolves
}
return (
<form action={action}>
{optimistic.map((m, i) => <p key={i}>{m.text}{m.sending && " (sending)"}</p>)}
<input name="text" />
</form>
);
}
Erreur fréquente : Appeler le setter optimiste hors d'une action ou d'une transition. React l'attend dans une action de formulaire ou dans startTransition ; ailleurs, la valeur optimiste apparaît et disparaît immédiatement (React émet un avertissement en développement). Et gérez toujours l'échec : si send lève une erreur, affichez-la pour que le message ne disparaisse pas en silence.
useCe qu'il fait : Lit la valeur d'une promise (en suspendant jusqu'à sa résolution) ou d'un contexte. Contrairement aux autres hooks, il peut être appelé dans des conditions et des boucles.
// React 19+
import { use, Suspense } from "react";
function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
const comments = use(commentsPromise); // suspends until resolved
return comments.map((c) => <p key={c.id}>{c.text}</p>);
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<p>Loading comments...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
// Conditional context read, which useContext cannot do:
function Heading({ show }: { show: boolean }) {
if (!show) return null;
const theme = use(ThemeContext);
return <h1 className={theme}>Hello</h1>;
}
Erreur fréquente : Créer la promise dans le composant qui appelle use, par exemple use(fetch(url)). Chaque rendu crée une nouvelle promise, donc le composant reste suspendu indéfiniment. Créez la promise une seule fois (dans un server component, un loader, un cache ou un parent) et transmettez-la. use ne peut pas non plus se trouver dans un try/catch ; gérez les erreurs avec une error boundary.
useFormStatusCe qu'il fait : Importé depuis react-dom. Lit le statut du <form> parent : pending, data, method et action.
// React 19+
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>{pending ? "Sending..." : "Send"}</button>;
}
function ContactForm() {
return (
<form action={sendMessage}>
<textarea name="message" />
<SubmitButton />
</form>
);
}
Erreur fréquente : L'appeler dans le composant même qui rend le <form>. Il ne renseigne que sur un formulaire au-dessus du composant appelant, donc dans le composant du formulaire lui-même pending vaut toujours false. Déplacez le bouton dans son propre composant, comme ci-dessus.
useEffect, useLayoutEffect, useMemo, useCallback and useImperativeHandle prennent une liste de valeurs comme dernier argument. React compare chaque élément avec la valeur du rendu précédent à l'aide de Object.is. Si un élément a changé, l'effet s'exécute à nouveau (ou le memo recalcule). Cette seule règle explique tous les cas :
| Vous écrivez | L'effet s'exécute | Usage typique |
|---|---|---|
| useEffect(fn) | après chaque rendu | rare ; signe généralement que quelque chose devrait être calculé pendant le rendu |
| useEffect(fn, []) | une fois après le montage (deux fois en Strict Mode de développement), nettoyage au démontage | abonnements et initialisations qui n'utilisent ni props ni état |
| useEffect(fn, [a, b]) | après le montage, puis chaque fois que a ou b change | chargement par ID, synchronisation avec une prop |
Cliquez sur les boutons : changer b relance uniquement l'effet sans tableau, changer a relance celui-ci ainsi que l'effet [a], et l'effet [] reste silencieux après le montage.
eslint-plugin-react-hooks et laissez exhaustive-deps vous signaler ce qui manque.{ id } ou () => {} n'est jamais égal au précédent selon Object.is, ils relancent donc l'effet à chaque rendu. Déplacez-les dans l'effet, hors du composant, ou mémoïsez-les.[user.id] est plus stable que [user] quand seul l'ID compte.setCount(c => c + 1) ne lit pas count, donc count n'a pas besoin d'être listé.useEffectEvent sur React 19.2+, ou une ref que vous maintenez à jour sur React 18.// Stale closure: logs 0 forever
useEffect(() => {
const id = setInterval(() => console.log(count), 1000);
return () => clearInterval(id);
}, []); // count is missing
// Fixed: re-subscribe when count changes
useEffect(() => {
const id = setInterval(() => console.log(count), 1000);
return () => clearInterval(id);
}, [count]);
// Fetch with cleanup so a slow old response cannot overwrite a newer one
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then((r) => r.json())
.then(setUser)
.catch((err) => { if (err.name !== "AbortError") setError(err); });
return () => controller.abort();
}, [userId]);
Un hook personnalisé est une fonction dont le nom commence par use et qui appelle d'autres hooks. Il permet à plusieurs composants de partager une logique avec état ; chaque composant qui l'appelle obtient son propre état indépendant. Les hooks ci-dessous ont été écrits pour cette page en TypeScript, fonctionnent sur React 18 et 19, et n'ont aucune dépendance. Chacun se termine par une note indiquant quand ne pas l'utiliser.
useDebounceRenvoie une valeur qui ne se met à jour qu'une fois que l'entrée a cessé de changer pendant delay millisecondes. Idéal pour les champs de recherche qui appellent une API.
import { useEffect, useState } from "react";
export function useDebounce<T>(value: T, delay = 300): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = window.setTimeout(() => setDebounced(value), delay);
return () => window.clearTimeout(id);
}, [value, delay]);
return debounced;
}
// const debouncedQuery = useDebounce(query, 400);
// useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);
Quand ne pas l'utiliser : Quand vous avez seulement besoin que l'interface reste réactive pendant le rendu d'une grande liste. C'est un problème de rendu, et useDeferredValue le résout sans délai artificiel. Évitez-le aussi pour une validation de formulaire qui doit sembler instantanée.
useLocalStorageFonctionne comme useState, mais enregistre la valeur dans localStorage et la restaure à la visite suivante.
import { useEffect, useState } from "react";
export function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
try {
const raw = window.localStorage.getItem(key);
return raw === null ? initial : (JSON.parse(raw) as T);
} catch {
return initial; // private mode, blocked storage or bad JSON
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch {
/* storage full or blocked: keep the in-memory value */
}
}, [key, value]);
return [value, setValue] as const;
}
Quand ne pas l'utiliser : Pour tout ce qui est sensible (tokens, données personnelles), puisque n'importe quel script de la page peut lire localStorage. Pas non plus sans précaution pour les pages rendues côté serveur : le serveur n'a pas de window, lisez donc le stockage après le montage pour éviter une incohérence d'hydratation. Il ne se synchronise pas entre onglets ; ajoutez un écouteur d'événement storage si vous en avez besoin.
useMediaQueryRenvoie true tant qu'une media query CSS correspond, et se met à jour quand cela change.
import { useCallback, useSyncExternalStore } from "react";
export function useMediaQuery(query: string): boolean {
const subscribe = useCallback((onChange: () => void) => {
const mql = window.matchMedia(query);
mql.addEventListener("change", onChange);
return () => mql.removeEventListener("change", onChange);
}, [query]);
return useSyncExternalStore(
subscribe,
() => window.matchMedia(query).matches,
() => false, // server snapshot
);
}
// const isDesktop = useMediaQuery("(min-width: 1024px)");
// const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
Quand ne pas l'utiliser : Pour des changements de mise en page que le CSS peut faire seul. Masquer ou réorganiser des éléments avec les breakpoints Tailwind (md:hidden) fonctionne avant le chargement du JavaScript et ne clignote jamais. Utilisez le hook quand vous devez rendre des composants différents ou éviter complètement de charger quelque chose.
useToggleUn booléen avec une fonction toggle stable, pour les menus, les modales et les sections dépliables.
import { useCallback, useState } from "react";
export function useToggle(initial = false) {
const [on, setOn] = useState(initial);
const toggle = useCallback(() => setOn((v) => !v), []);
return [on, toggle, setOn] as const;
}
// const [open, toggleOpen, setOpen] = useToggle();
Quand ne pas l'utiliser : Quand l'état a plus de deux valeurs (fermé, en ouverture, ouvert, erreur). Utilisez une union de chaînes avec useState ou useReducer au lieu d'empiler plusieurs toggles.
usePreviousRenvoie la valeur du rendu précédent, pratique pour afficher une évolution (« en hausse par rapport à 12 ») ou réagir à une transition.
import { useEffect, useRef } from "react";
export function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Quand ne pas l'utiliser : Pour décider s'il faut mettre à jour l'état. La documentation de React montre un pattern plus propre pour cela : stocker la valeur précédente dans l'état et comparer pendant le rendu. Ce hook lit une ref pendant le rendu, ce qui fonctionne aujourd'hui mais relève du type de code que les recommandations de React demandent de garder rare.
useOnClickOutsideAppelle un gestionnaire quand l'utilisateur appuie n'importe où en dehors d'un élément donné. Typique pour fermer un menu ou un popover personnalisé.
import { useEffect, useRef, type RefObject } from "react";
export function useOnClickOutside(
ref: RefObject<HTMLElement>,
handler: (event: PointerEvent) => void,
) {
const saved = useRef(handler);
useEffect(() => { saved.current = handler; }, [handler]);
useEffect(() => {
const listener = (event: PointerEvent) => {
if (!ref.current || ref.current.contains(event.target as Node)) return;
saved.current(event);
};
document.addEventListener("pointerdown", listener);
return () => document.removeEventListener("pointerdown", listener);
}, [ref]);
}
Quand ne pas l'utiliser : Si vous utilisez déjà une bibliothèque headless (Radix, Headless UI) pour le menu ou la boîte de dialogue : elles gèrent pour vous les clics extérieurs, la touche Escape et le retour du focus. Un menu fait main reposant uniquement sur ce hook oublie généralement la prise en charge du clavier.
useIntervalUn setInterval qui appelle toujours le dernier callback et peut être mis en pause en passant null comme délai.
import { useEffect, useRef } from "react";
export function useInterval(callback: () => void, delay: number | null) {
const saved = useRef(callback);
useEffect(() => { saved.current = callback; }, [callback]);
useEffect(() => {
if (delay === null) return;
const id = window.setInterval(() => saved.current(), delay);
return () => window.clearInterval(id);
}, [delay]);
}
// useInterval(() => setTicks((t) => t + 1), running ? 1000 : null);
Quand ne pas l'utiliser : Pour les animations (utilisez CSS ou requestAnimationFrame) ou pour des horloges précises : les intervalles dérivent et les navigateurs les ralentissent dans les onglets en arrière-plan. Pour un compte à rebours, calculez le temps restant à partir d'un timestamp de fin fixe à chaque tick.
useWindowSizeRenvoie la largeur et la hauteur actuelles de la fenêtre et se met à jour au redimensionnement.
import { useSyncExternalStore } from "react";
function subscribe(onChange: () => void) {
window.addEventListener("resize", onChange);
return () => window.removeEventListener("resize", onChange);
}
export function useWindowSize() {
const width = useSyncExternalStore(subscribe, () => window.innerWidth, () => 0);
const height = useSyncExternalStore(subscribe, () => window.innerHeight, () => 0);
return { width, height };
}
Quand ne pas l'utiliser : Pour les breakpoints : useMediaQuery ne refait le rendu que lorsque la query bascule, alors que celui-ci refait le rendu à chaque pixel de redimensionnement. Pour la taille d'un élément plutôt que de la fenêtre, utilisez un ResizeObserver. (Les deux snapshots séparés sont voulus : renvoyer un nouvel objet depuis une seule fonction de snapshot bouclerait indéfiniment.)
useCopyToClipboardCopie du texte avec la Clipboard API et expose un indicateur copied de courte durée pour le retour visuel du bouton.
import { useCallback, useState } from "react";
export function useCopyToClipboard(resetAfter = 1500) {
const [copied, setCopied] = useState(false);
const copy = useCallback(async (text: string) => {
try {
await navigator.clipboard.writeText(text);
setCopied(true);
window.setTimeout(() => setCopied(false), resetAfter);
return true;
} catch {
setCopied(false); // insecure context or permission denied
return false;
}
}, [resetAfter]);
return [copied, copy] as const;
}
Quand ne pas l'utiliser : Sur des pages servies en HTTP simple : navigator.clipboard nécessite un contexte sécurisé (HTTPS ou localhost). Proposez toujours une solution de repli, comme sélectionner le texte, quand il renvoie false.
useFetchCharge le JSON d'une URL, suit l'état de chargement et d'erreur, et annule la requête quand l'URL change ou que le composant est démonté.
import { useEffect, useState } from "react";
type FetchState<T> = { data: T | null; error: Error | null; loading: boolean };
export function useFetch<T>(url: string | null): FetchState<T> {
const [state, setState] = useState<FetchState<T>>({ data: null, error: null, loading: !!url });
useEffect(() => {
if (!url) return;
const controller = new AbortController();
setState((s) => ({ ...s, loading: true, error: null }));
fetch(url, { signal: controller.signal })
.then((res) => {
if (!res.ok) throw new Error(`Request failed with status ${res.status}`);
return res.json() as Promise<T>;
})
.then((data) => setState({ data, error: null, loading: false }))
.catch((error: Error) => {
if (error.name === "AbortError") return; // superseded or unmounted
setState({ data: null, error, loading: false });
});
return () => controller.abort();
}, [url]);
return state;
}
Quand ne pas l'utiliser : Dans les applications plus importantes. Il n'a ni cache, ni déduplication, ni nouvelles tentatives, ni revalidation, donc deux composants qui demandent la même URL font deux requêtes. Utilisez le chargement de données de votre framework ou une bibliothèque de données (TanStack Query, SWR) dès que vous avez besoin de l'un de ces éléments. La documentation de React fait la même recommandation pour le chargement de données dans les effets.
useEventListenerAttache un écouteur d'événement à la fenêtre (ou à n'importe quel élément) et appelle toujours le dernier gestionnaire, sans se réabonner à chaque rendu.
import { useEffect, useRef } from "react";
export function useEventListener<K extends keyof WindowEventMap>(
type: K,
handler: (event: WindowEventMap[K]) => void,
target: Window | HTMLElement | null = typeof window === "undefined" ? null : window,
) {
const saved = useRef(handler);
useEffect(() => { saved.current = handler; }, [handler]);
useEffect(() => {
if (!target) return;
const listener = (event: Event) => saved.current(event as WindowEventMap[K]);
target.addEventListener(type, listener);
return () => target.removeEventListener(type, listener);
}, [type, target]);
}
// useEventListener("keydown", (e) => { if (e.key === "Escape") close(); });
Quand ne pas l'utiliser : Pour les événements que React gère déjà sur les éléments que vous rendez : utilisez onClick, onKeyDown et consorts directement dans le JSX. N'utilisez ce hook que pour window, document ou des éléments tiers.
useIsMountedRenvoie une fonction qui vous indique si le composant est toujours à l'écran, pour du code async ancien qui modifie l'état après un await.
import { useCallback, useEffect, useRef } from "react";
export function useIsMounted() {
const mounted = useRef(false);
useEffect(() => {
mounted.current = true;
return () => { mounted.current = false; };
}, []);
return useCallback(() => mounted.current, []);
}
// const isMounted = useIsMounted();
// const data = await load(); if (isMounted()) setData(data);
Quand ne pas l'utiliser : Par défaut. React 18 a supprimé l'avertissement sur la modification d'état de composants démontés, et la meilleure solution consiste à annuler le travail lui-même (un AbortController, comme dans useFetch) ou à ignorer les résultats périmés avec un indicateur dans le nettoyage de l'effet. Réservez-le à du code ancien que vous ne pouvez pas encore restructurer.
useState (ou useReducer si plusieurs valeurs changent ensemble).useRef.useContext, ou un store externe lu avec useSyncExternalStore.useEffect ; passez à useLayoutEffect uniquement pour les mesures qui provoquent un scintillement visible.useMemo. Un enfant mémoïsé continue de refaire son rendu à cause d'une prop fonction : useCallback.useDeferredValue ou useTransition.useActionState, useFormStatus, useOptimistic.Vous construisez une bibliothèque de composants plutôt que des hooks isolés ? Notre comparatif des bibliothèques UI React montre les six mêmes composants dans shadcn/ui, Radix, Headless UI, daisyUI, Flowbite et MUI, et l'aide-mémoire Tailwind couvre les classes utilitaires utilisées dans les démos de cette page. Des éléments React et Tailwind prêts à l'emploi se trouvent dans la galerie de composants.
Au moment où nous écrivons ces lignes (octobre 2026, React 19.3), la référence des hooks React liste 17 hooks dans React lui-même : useState, useReducer, useContext, useRef, useImperativeHandle, useEffect, useLayoutEffect, useInsertionEffect, useEffectEvent, useMemo, useCallback, useTransition, useDeferredValue, useDebugValue, useId, useSyncExternalStore et useActionState. React 19 ajoute aussi useOptimistic et l'API use, et React DOM ajoute useFormStatus.
useMemo met en cache le résultat de l'appel d'une fonction, tandis que useCallback met en cache la fonction elle-même. useCallback(fn, deps) équivaut à useMemo(() => fn, deps). Utilisez useMemo pour les valeurs coûteuses et useCallback pour les fonctions passées à des enfants mémoïsés ou utilisées comme dépendances d'effet.
En développement avec le Strict Mode activé, React monte chaque composant, le démonte puis le remonte pour vous aider à repérer les effets auxquels il manque un nettoyage. Cela n'arrive pas dans un build de production. Si la double exécution provoque un bug, ajoutez une fonction de nettoyage qui annule ce que l'effet a lancé.
Un tableau vide indique à React que l'effet ne lit ni props ni état : il s'exécute donc une fois après le montage du composant et son nettoyage s'exécute au démontage. Si l'effet lit des props ou de l'état, listez-les, sinon il continue d'utiliser les valeurs du premier rendu.
Non. use, useActionState, useOptimistic et useFormStatus ne sont pas exportés par React 18, et useEffectEvent nécessite React 19.2 ou une version ultérieure. Sur React 18, vous pouvez obtenir un comportement similaire avec useState et des gestionnaires d'événements pour les formulaires, et avec une ref qui contient le dernier callback à la place de useEffectEvent.
Son nom commence par use et elle appelle au moins un autre hook. Ce nommage permet aux règles de lint de React de vérifier qu'elle respecte les règles des hooks. Chaque composant qui appelle un hook personnalisé obtient son propre état séparé ; les hooks partagent de la logique, pas des données.
En direct : {title}
+3 (updater)
Réinitialiser
Lire la section useReducer
Ajouter votre propre élément
Nouvel élément
Nouvelle tâche
Effacer les terminées
Je lis « {theme} » depuis le contexte
Basculer la valeur du provider
Cliquez sur le bouton pour me donner le focus
Cible du focus
Focus sur le champ
Ajouter à la ref (sans rendu)
Refaire le rendu
Valeur de la ref affichée au dernier rendu : {n}
Tapez ici
Champ enfant
Le parent appelle shake()
Le parent appelle clear()
effet exécuté : intervalle démarré
nettoyage exécuté : intervalle arrêté
Pause
Reprendre
Redimensionnez-moi en tapant
Texte à mesurer
Mesuré avant affichage : {n}px de large
Couleur de bordure
Cette règle a été injectée dans le head de la page
Filtrer 4,000 éléments
Filtrer les éléments
Rendu sans rapport ({n})
{n} résultats. Le filtre a tourné {runs} fois (les rendus sans rapport ne le relancent pas).
{label} (enfant rendu {n}x)
fonction en ligne
Refaire le rendu du parent
Compteur : {n}
À propos
Liste lente
Contact
Changement en cours (l'ancien onglet reste cliquable)...
Affichage : {tab}
Ligne
Contenu de l'onglet {tab}.
Résultats rendus pour : « {query} »
Tapez vite : le champ reste réactif
Rechercher
Nom
Réseau :
Largeur de la fenêtre :
Redimensionnez la fenêtre ou coupez votre réseau pour voir la mise à jour.
Ce composant appelle un hook personnalisé qui s'étiquette « {label} » dans React DevTools. Ouvrez l'onglet Components pour voir l'étiquette à côté du hook.
En ligne
Hors ligne
tableaux de dépendances
[sans tableau] exécuté (rendu {n})
[] exécuté une fois au montage
[a] exécuté, a = {n}
Changer a ({n})
Changer b ({n})
Effacer le journal
Tapez, puis arrêtez une demi-seconde
Champ avec debounce
Instantané : « {text} » · Avec debounce : « {debounced} »
Tapez, puis rechargez la page
Nom enregistré
Enregistré uniquement dans ce navigateur.
Enregistré uniquement dans ce navigateur : « {name} ».
useMediaQuery : le viewport fait {size}, le thème système est {theme}.
768px ou plus
moins de 768px
Activé
Désactivé
Compteur {n}
précédent : {value}
rien pour l'instant
Arrêter
Démarrer
{n} ticks
Copié
Copier une commande
Ouvrir le menu
Cliquez n'importe où en dehors du menu pour le fermer.
Commencer gratuitement · Sans carte bancaire