Votre application React rame ? Re-renders intempestifs, bundle trop lourd, interactions lentes ? Les problèmes de performance React sont courants mais solubles. Voici les techniques d'optimisation qui font vraiment la différence.

Comprendre les re-renders

React re-rend un composant quand :

  • Son state change
  • Ses props changent
  • Son parent re-rend (même si les props n'ont pas changé)

Le problème : des re-renders inutiles qui cascadent dans tout l'arbre de composants.

React DevTools Profiler

Avant d'optimiser, mesurez. Le Profiler montre :

  • Quels composants re-rendent
  • Combien de temps prend chaque render
  • Pourquoi un composant a re-rendu

Règle d'or : optimisez ce qui est mesuré comme problématique, pas ce qui vous semble suspect.

Mémoization des composants

React.memo

Empêche un composant de re-rendre si ses props n'ont pas changé :

const ExpensiveComponent = React.memo(({ data }) => {
  // render coûteux
  return <div>{/* ... */}</div>;
});

Attention : memo fait une comparaison shallow des props. Les objets et tableaux recréés à chaque render invalident le memo.

useMemo

Mémoize une valeur calculée :

const sortedItems = useMemo(() => {
  return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

Utile pour : calculs coûteux, transformation de données, objets passés en props.

useCallback

Mémoize une fonction pour éviter de casser le memo d'un composant enfant :

const handleClick = useCallback(() => {
  doSomething(id);
}, [id]);

Utile quand la fonction est passée en prop à un composant mémoizé.

Gestion du state

Colocate state

Le state doit vivre au plus près de là où il est utilisé. Un state haut dans l'arbre cause des re-renders de tout ce qui est en dessous.

Découper les composants

Isolez les parties qui changent souvent :

// Mauvais : tout re-rend quand le timer change
function Page() {
  const [time, setTime] = useState(new Date());
  return (
    <div>
      <Header />
      <Timer time={time} />
      <ExpensiveContent />
    </div>
  );
}

// Mieux : seul Timer re-rend
function Page() {
  return (
    <div>
      <Header />
      <TimerContainer />
      <ExpensiveContent />
    </div>
  );
}

State management externe

Pour le state global, utilisez des solutions qui permettent des subscriptions granulaires :

  • Zustand : simple et performant
  • Jotai : atomique, très granulaire
  • Redux Toolkit : avec selectors optimisés

Optimisation des listes

Keys stables

Utilisez des identifiants uniques et stables, jamais l'index :

// Mauvais
items.map((item, index) => <Item key={index} />)

// Bon
items.map((item) => <Item key={item.id} />)

Virtualisation

Pour les longues listes, ne rendez que les éléments visibles :

  • react-window : léger et efficace
  • react-virtuoso : plus de fonctionnalités
  • TanStack Virtual : headless, très flexible

Lazy loading

React.lazy

Chargez les composants lourds à la demande :

const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <HeavyComponent />
    </Suspense>
  );
}

Route-based splitting

Avec React Router, chaque route peut être un chunk séparé :

const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Settings = React.lazy(() => import('./pages/Settings'));

Optimisation du bundle

Analyser le bundle

Utilisez webpack-bundle-analyzer ou source-map-explorer pour visualiser ce qui prend de la place.

Tree shaking

Importez uniquement ce dont vous avez besoin :

// Mauvais : importe toute la lib
import _ from 'lodash';

// Bon : importe seulement la fonction
import debounce from 'lodash/debounce';

Dependencies légères

Alternatives légères aux libs courantes :

  • date-fns au lieu de moment
  • clsx au lieu de classnames
  • zustand au lieu de redux (si adapté)

Images et assets

  • Format WebP/AVIF
  • Lazy loading natif (loading="lazy")
  • srcset pour le responsive
  • Placeholder blur pendant le chargement

Server Components (React 18+)

Avec Next.js App Router, les Server Components :

  • Rendent côté serveur sans JS client
  • Réduisent drastiquement le bundle
  • Accèdent directement aux données (pas de useEffect)

Métriques à surveiller

  • LCP : temps d'affichage du contenu principal
  • INP : réactivité aux interactions
  • Bundle size : taille du JavaScript chargé
  • Time to Interactive : moment où l'app devient utilisable

Conclusion

La performance React n'est pas de la magie. C'est une combinaison de bonnes pratiques : éviter les re-renders inutiles, charger le code à la demande, et mesurer avant d'optimiser. Commencez par le Profiler, identifiez les vrais problèmes, puis appliquez les solutions appropriées.

Partager cet article

Besoin d'aide sur ce sujet ?

Nos experts peuvent vous accompagner.

Appeler > Démarrer