Patterns avancés
HOC, render props et custom hooks
Custom Hooks
Custom Hooks
Un custom hook est une fonction commençant par use qui encapsule de la logique réutilisable avec des hooks React.
useLocalStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
} catch { return initialValue; }
});
const setStoredValue = (newValue) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
};
return [value, setStoredValue];
}
// Usage
const [theme, setTheme] = useLocalStorage('theme', 'light');
useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
fetch(url, { signal: controller.signal })
.then(res => { if (!res.ok) throw new Error(res.statusText); return res.json(); })
.then(data => { setData(data); setLoading(false); })
.catch(err => { if (err.name !== 'AbortError') { setError(err); setLoading(false); } });
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Usage
const { data: users, loading } = useFetch('/api/users');
useDebounce
function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage : recherche sans spammer l'API
function Recherche() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
const { data } = useFetch(`/api/search?q=${debouncedQuery}`);
}
HOC et Render Props
Higher-Order Components (HOC)
Un HOC est une fonction qui prend un composant et retourne un nouveau composant enrichi. Pattern issu des classes, encore utilisé pour des wrappers génériques.
// withAuth — HOC de protection de route
function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const { user } = useAuth();
if (!user) return <Navigate to="/login" />;
return <WrappedComponent {...props} user={user} />;
};
}
// Usage
const ProtectedDashboard = withAuth(Dashboard);
// withLoading — HOC d'affichage de chargement
function withLoading(WrappedComponent) {
return function ({ isLoading, ...props }) {
if (isLoading) return <Spinner />;
return <WrappedComponent {...props} />;
};
}
Render Props
Pattern où un composant reçoit une fonction en prop et l'appelle pour déléguer le rendu. Remplacé en grande partie par les custom hooks.
// DataFetcher avec render prop
function DataFetcher({ url, render }) {
const { data, loading, error } = useFetch(url);
return render({ data, loading, error });
}
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading ? <Spinner /> : <UserList users={data} />
)}
/>
// Version children-as-function
<DataFetcher url="/api/users">
{({ data, loading }) => loading ? <Spinner /> : <UserList users={data} />}
</DataFetcher>
Recommandation actuelle
Dans React moderne, les custom hooks couvrent la majorité des cas d'usage des HOC et render props avec moins de complexité et de nesting. Utiliser HOC/render props principalement pour l'intégration avec des bibliothèques tierces ou la compatibilité legacy.