⚛️ Comment créer un hook React personnalisé (guide complet avec TypeScript + exemples)
Si vous développez en React, vous avez forcément déjà :
- copié-collé du useEffect
- dupliqué du useState
- répété des appels API
👉 C’est exactement pour éviter ça qu’existent les hooks React personnalisés.
Dans ce guide complet, vous allez apprendre :
- Comment créer un hook React personnalisé
- Les règles essentielles à respecter
- Des exemples concrets en TypeScript
- Les erreurs fréquentes à éviter
- Comment optimiser vos hooks pour la performance
TL;DR – C’est quoi un hook React personnalisé ?
Un hook React personnalisé est :
- une fonction JavaScript
- qui commence par "use"
- qui utilise des hooks React (useState, useEffect, etc.)
- qui encapsule une logique réutilisable
👉 Objectif : réutiliser du code proprement et améliorer l’architecture de votre application.
Pourquoi créer un hook React personnalisé ?
Sans hook
- code dupliqué
- logique dispersée
- maintenance compliquée
Avec hook
- logique centralisée
- réutilisable
- testable
- plus lisible
👉 C’est une base essentielle pour une architecture React moderne.
Exemple 1 – Hook simple : useCounter
import { useState } from "react";
export function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
Utilisation
function Counter() {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
Exemple 2 – useLocalStorage (persistant)
Cas d’usage : sauvegarder des préférences utilisateur.
import { useState } from "react";
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === "undefined") return initialValue;
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== "undefined") {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
};
return [storedValue, setValue] as const;
}
⚠️ Important avec Next.js : toujours vérifier que window existe (SSR).
Exemple 3 – Hook API : useApi
import { useState, useEffect } from "react";
export function useApi<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error("Network error");
const result = await response.json();
setData(result);
} catch (err) {
if ((err as any).name !== "AbortError") {
setError(err as Error);
}
} finally {
setLoading(false);
}
};
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
Exemple 4 – Performance : useDebounce
import { useEffect, useState } from "react";
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
Règles importantes des hooks React
- ne jamais appeler un hook dans une condition
- respecter l’ordre d’appel
- toujours commencer par "use"
- gérer correctement les dépendances
👉 Sinon : erreur "Invalid Hook Call"
Erreurs fréquentes
- hook trop complexe
- trop de responsabilités
- dépendances mal gérées
- oublier le SSR
- ne pas tester
Tester un hook React
import { renderHook, act } from "@testing-library/react";
import { useCounter } from "./useCounter";
test("should increment counter", () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
FAQ – Hooks React personnalisés
Comment créer un hook React personnalisé ?
Créer une fonction qui commence par "use" et encapsule une logique réutilisable.
Peut-on utiliser TypeScript ?
Oui, c’est recommandé.
Compatible avec Next.js ?
Oui, mais attention au SSR (window, localStorage).
Différence hook vs composant ?
- hook = logique
- composant = interface
Conclusion
Maîtriser les hooks React personnalisés permet de :
- structurer votre application
- réduire la duplication
- améliorer les performances
- créer une architecture scalable
👉 C’est une compétence essentielle pour tout développeur React moderne.
Besoin d’aller plus loin ?
👉 Vous souhaitez optimiser votre architecture React ou Next.js ?
