📌 useRef
useRef crée une référence mutable qui persiste entre les rendus sans déclencher de re-render quand elle change.
import { useRef, useEffect } from 'react';
function InputAutoFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // accès direct au DOM
}, []);
return <input ref={inputRef} placeholder="Auto-focus" />;
}
// Stocker une valeur sans re-render
function Chronometre() {
const [actif, setActif] = useState(false);
const [temps, setTemps] = useState(0);
const intervalRef = useRef(null); // stocke l'ID sans re-render
function demarrer() {
setActif(true);
intervalRef.current = setInterval(() => setTemps(t => t + 1), 1000);
}
function arreter() {
setActif(false);
clearInterval(intervalRef.current);
}
}
🧮 useMemo — mémoïsation de valeurs
useMemo mémoïse le résultat d'un calcul coûteux et ne le recalcule que si ses dépendances changent.
import { useMemo } from 'react';
function ListeFiltrée({ items, filtre }) {
// Calcul coûteux — mémoïsé
const resultats = useMemo(() => {
console.log('Calcul filtrage...');
return items
.filter(i => i.nom.includes(filtre))
.sort((a, b) => a.prix - b.prix);
}, [items, filtre]); // recalcule seulement si items ou filtre change
return <ul>{resultats.map(i => <li key={i.id}>{i.nom}</li>)}</ul>;
}
✅ Ne pas abuser de
useMemo — l'overhead de mémoïsation peut coûter plus cher que le recalcul pour des opérations simples. Réservez-le aux calculs vraiment coûteux.🔄 useCallback — mémoïsation de fonctions
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// Sans useCallback — nouvelle référence à chaque render
// const handleClick = () => console.log('clic');
// Avec useCallback — même référence si count ne change pas
const handleClick = useCallback(() => {
console.log('count :', count);
}, [count]);
return <Enfant onClick={handleClick} />;
}
// Enfant enveloppé dans React.memo
const Enfant = React.memo(function Enfant({ onClick }) {
console.log('Enfant rendu');
return <button onClick={onClick}>Clic</button>;
});
// Sans useCallback + memo, Enfant se re-render à chaque render du parent
🛡️ React.memo
// React.memo évite le re-render si les props n'ont pas changé
const CarteBlog = React.memo(function CarteBlog({ titre, auteur }) {
console.log('CarteBlog rendu :', titre);
return (
<div>
<h3>{titre}</h3>
<p>{auteur}</p>
</div>
);
});
// Comparaison personnalisée (optionnel)
const CarteBlogOpt = React.memo(CarteBlog, (prevProps, nextProps) => {
return prevProps.titre === nextProps.titre; // vrai = pas de re-render
});
⚙️ useReducer
import { useReducer } from 'react';
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
switch (action.type) {
case 'increment': return { ...state, count: state.count + state.step };
case 'decrement': return { ...state, count: state.count - state.step };
case 'reset': return initialState;
case 'setStep': return { ...state, step: action.payload };
default: return state;
}
}
function Compteur() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>{state.count} (step: {state.step})</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'setStep', payload: 5 })}>Step=5</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}