14 — Custom Hooks

🔍 C'est quoi un Custom Hook ?

Un custom hook est une fonction JavaScript qui commence par use et qui utilise des hooks React à l'intérieur. C'est un moyen d'extraire et réutiliser de la logique entre composants.

// Custom hook = fonction qui commence par "use"
function useMonHook() {
    const [valeur, setValeur] = useState(initial);
    
    // Logique réutilisable...
    
    return { valeur, setValeur }; // Retourne ce qu'on veut
}

📐 Pourquoi les Custom Hooks ?

Quand plusieurs composants ont la même logique (mais pas la même UI), on l'extrait dans un hook :

🔨 Exemples concrets

1. useToggle — gérer un booléen

function useToggle(initial = false) {
    const [value, setValue] = useState(initial);
    
    const toggle = () => setValue(prev => !prev);
    const setTrue = () => setValue(true);
    const setFalse = () => setValue(false);
    
    return { value, toggle, setTrue, setFalse };
}

// Utilisation
function Modal() {
    const { value: isOpen, toggle, setFalse: close } = useToggle();
    return (
        <>
            <button onClick={toggle}>Ouvrir/Fermer</button>
            {isOpen && <div className="modal">
                Contenu...
                <button onClick={close}>Fermer</button>
            </div>}
        </>
    );
}

2. useLocalStorage — état persisté

function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
        const saved = localStorage.getItem(key);
        return saved !== null ? JSON.parse(saved) : initialValue;
    });

    useEffect(() => {
        localStorage.setItem(key, JSON.stringify(value));
    }, [key, value]);

    return [value, setValue];
}

// Utilisation
function App() {
    const [nom, setNom] = useLocalStorage('nom', 'Alice');
    // La valeur survit au rechargement de la page !
}

3. useFetch — appels API

function useFetch(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        setLoading(true);
        fetch(url)
            .then(res => {
                if (!res.ok) throw new Error('Erreur réseau');
                return res.json();
            })
            .then(data => { setData(data); setLoading(false); })
            .catch(err => { setError(err.message); setLoading(false); });
    }, [url]);

    return { data, loading, error };
}

// Utilisation — ultra simple !
function ListeUsers() {
    const { data, loading, error } = useFetch('https://api.example.com/users');
    
    if (loading) return <p>Chargement...</p>;
    if (error) return <p>Erreur : {error}</p>;
    return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

4. useWindowSize — taille de la fenêtre

function useWindowSize() {
    const [size, setSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight
    });

    useEffect(() => {
        const handler = () => setSize({
            width: window.innerWidth,
            height: window.innerHeight
        });
        window.addEventListener('resize', handler);
        return () => window.removeEventListener('resize', handler);
    }, []);

    return size;
}

📏 Règles des Custom Hooks

  1. Le nom commence par use (convention obligatoire).
  2. Peut appeler d'autres hooks (useState, useEffect, d'autres custom hooks).
  3. Suit les mêmes règles que les hooks normaux (top level uniquement).
  4. Chaque composant qui utilise le hook a son propre état (pas partagé).

🚀 Démo interactive

▼ Custom Hooks en action

📝 Résumé

← 13 — useContext 15 — Projet Todo →