{post.title}
{post.body}

Découvrez des exemples pratiques de Hooks personnalisés (Custom Hooks) React populaires : useFetch, useLocalStorage, useToggle, useFormInput avec leur code et utilisation.
Après avoir compris la motivation et les règles de création des Hooks personnalisés, rien ne vaut des exemples concrets pour saisir leur utilité et leur fonctionnement. Nous allons explorer ici quatre exemples courants de Hooks personnalisés qui résolvent des problèmes récurrents dans le développement React :
useFetch : Pour la récupération de données asynchrone.useLocalStorage : Pour la persistance de l'état dans le stockage local du navigateur.useToggle : Pour gérer facilement un état booléen.useFormInput : Pour simplifier la gestion de l'état des champs de formulaire.Ces exemples illustrent comment encapsuler une logique spécifique et la rendre réutilisable à travers différents composants.
Objectif : Encapsuler la logique de récupération de données depuis une URL, en gérant les états de chargement, les données reçues et les erreurs éventuelles.
import { useState, useEffect } from 'react';
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) {
setIsLoading(false);
setData(null);
setError(null);
return;
}
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
setIsLoading(true);
setData(null);
setError(null);
try {
const response = await fetch(url, { ...options, signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!signal.aborted) {
setData(result);
}
} catch (fetchError) {
if (fetchError.name !== 'AbortError' && !signal.aborted) {
setError(fetchError.message);
}
} finally {
if (!signal.aborted) {
setIsLoading(false);
}
}
};
fetchData();
// Cleanup function to abort fetch on unmount or url change
return () => {
controller.abort();
};
}, [url, JSON.stringify(options)]); // Re-run if url or options change (stringify for deep comparison)
return { data, isLoading, error };
}
export default useFetch;
import React from 'react';
import useFetch from './hooks/useFetch';
function PostDisplay({ postId }) {
const { data: post, isLoading, error } = useFetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
if (isLoading) return Chargement du post...
;
if (error) return Erreur : {error}
;
if (!post) return null;
return (
{post.title}
{post.body}
);
}
Objectif : Fournir une interface similaire à `useState`, mais qui synchronise automatiquement la valeur avec le `localStorage` du navigateur.
import { useState, useEffect } from 'react';
function getStorageValue(key, defaultValue) {
if (typeof window === 'undefined') return defaultValue; // Pour SSR
const saved = localStorage.getItem(key);
try {
return saved !== null ? JSON.parse(saved) : defaultValue;
} catch (error) {
console.error("Error parsing localStorage key “", key, "”:", error);
return defaultValue;
}
}
function useLocalStorage(key, defaultValue) {
const [value, setValue] = useState(() => {
return getStorageValue(key, defaultValue);
});
useEffect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem(key, JSON.stringify(value));
}
}, [key, value]);
return [value, setValue];
}
export default useLocalStorage;
import React from 'react';
import useLocalStorage from './hooks/useLocalStorage';
function UserPreferences() {
const [username, setUsername] = useLocalStorage('username', 'Invité');
return (
Nom stocké : {username}
);
}
Objectif : Simplifier la gestion d'un état booléen en fournissant directement une fonction pour inverser sa valeur.
import { useState, useCallback } from 'react';
function useToggle(initialState = false) {
const [state, setState] = useState(initialState);
// Utilise useCallback pour mémoriser la fonction toggle
const toggle = useCallback(() => {
setState(prevState => !prevState);
}, []);
// Retourne l'état et la fonction toggle
return [state, toggle];
}
export default useToggle;
import React from 'react';
import useToggle from './hooks/useToggle';
function CollapsibleSection({ title, children }) {
const [isOpen, toggleOpen] = useToggle(false); // Initialement fermé
return (
{title} {isOpen ? '[-]' : '[+]'}
{isOpen && {children}}
);
}
Objectif : Encapsuler la logique de base pour un champ de formulaire contrôlé (valeur + gestionnaire `onChange`).
import { useState, useCallback } from 'react';
function useFormInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
const handleChange = useCallback((event) => {
setValue(event.target.value);
}, []);
// Retourne la valeur et le gestionnaire pour les props de l'input
// et potentiellement une fonction reset
const reset = useCallback(() => setValue(initialValue), [initialValue]);
return {
value, // Pour la prop 'value' de l'input
onChange: handleChange, // Pour la prop 'onChange' de l'input
reset // Fonction pour réinitialiser
};
}
export default useFormInput;
import React from 'react';
import useFormInput from './hooks/useFormInput';
function SimpleForm() {
const nameInputProps = useFormInput('John Doe');
const emailInputProps = useFormInput('');
const handleSubmit = (event) => {
event.preventDefault();
alert(`Nom: ${nameInputProps.value}, Email: ${emailInputProps.value}`);
// nameInputProps.reset(); // Optionnel : réinitialiser après soumission
// emailInputProps.reset();
};
return (
);
}
Ces exemples montrent comment les Hooks personnalisés peuvent encapsuler des logiques variées, de la récupération de données complexe à la simple gestion d'un booléen. En créant ces fonctions réutilisables, vous simplifiez vos composants, réduisez la duplication de code et rendez votre application plus facile à comprendre et à maintenir.
N'hésitez pas à créer vos propres Hooks personnalisés dès que vous identifiez une logique stateful répétitive dans votre code. C'est une pratique fondamentale pour écrire du code React propre et efficace.