📌 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>
  );
}
📝 Exercices R08 ▶ Mini-projet : Perf Demo 🧠 QCM R08 Suivant : Custom Hooks →