
Initialisation paresseuse (Lazy initial state)
Découvrez comment utiliser l'initialisation paresseuse (lazy initial state) avec le hook useState de React pour améliorer les performances en évitant les calculs initiaux coûteux.
Le problème : Le coût de l'initialisation répétée
Le hook `useState` prend un argument pour définir la valeur initiale de l'état. Par défaut, si vous passez une valeur directement, cette valeur (ou le résultat de l'appel de fonction qui la produit) est évaluée à chaque rendu du composant. Dans la plupart des cas, c'est anodin car l'initialisation est rapide.
Cependant, imaginez un scénario où la détermination de la valeur initiale nécessite une opération coûteuse : lire des données depuis le `localStorage`, effectuer une boucle complexe, analyser une grande quantité de données, etc. Exécuter ce calcul lourd à chaque fois que le composant se met à jour (même si la valeur initiale n'est pertinente que la toute première fois) représente un gaspillage de ressources et peut impacter négativement les performances de votre application, surtout pour des composants fréquemment rendus.
La solution : L'initialisation paresseuse avec une fonction
Pour pallier ce problème, React permet une forme d'initialisation dite "paresseuse" (lazy initialization). Au lieu de passer directement la valeur initiale à `useState`, vous pouvez lui passer une fonction. Cette fonction ne sera exécutée par React qu'une seule fois, lors du tout premier rendu du composant (au montage), pour déterminer la valeur initiale de l'état. Lors des rendus suivants, React ignorera simplement cette fonction et utilisera la valeur d'état actuelle.
Cette approche garantit que le calcul potentiellement coûteux n'est effectué qu'une seule fois, au moment où il est réellement nécessaire, optimisant ainsi les performances en évitant des exécutions superflues lors des mises à jour ultérieures du composant.
Voici la syntaxe comparative :
import React, { useState } from 'react';
// Fonction simulant un calcul initial coûteux
function computeExpensiveValue() {
console.log("Calcul initial coûteux exécuté !");
// Imaginez ici une opération complexe...
let result = 0;
for (let i = 0; i < 1e7; i++) {
result += i % 10;
}
return result;
}
function MonComposant() {
// --- Approche 1 : Initialisation standard (NON RECOMMANDEE si coûteux) ---
// const [valeurStandard, setValeurStandard] = useState(computeExpensiveValue());
// 'computeExpensiveValue()' serait appelé à CHAQUE rendu.
// --- Approche 2 : Initialisation Paresseuse (RECOMMANDEE si coûteux) ---
const [valeurParesseuse, setValeurParesseuse] = useState(() => {
// La fonction passée à useState n'est exécutée qu'au premier rendu.
return computeExpensiveValue();
});
const [count, setCount] = useState(0); // Un autre état pour provoquer des re-renders
console.log("Rendu du composant MonComposant");
return (
Valeur (Paresseuse): {valeurParesseuse}
Compteur pour re-render : {count}
);
}
export default MonComposant;
Si vous exécutez ce code en utilisant l'approche 2 (paresseuse), vous remarquerez dans la console que "Calcul initial coûteux exécuté !" n'apparaît qu'une seule fois, au chargement initial. En revanche, si vous décommentez l'approche 1, ce message apparaîtrait à chaque clic sur le bouton "Incrémenter le compteur", démontrant l'exécution répétée et inutile du calcul.
Quand utiliser l'initialisation paresseuse ?
L'initialisation paresseuse est particulièrement indiquée lorsque :
- Le calcul de la valeur initiale est sensiblement coûteux en termes de temps CPU ou de ressources.
- La valeur initiale est lue depuis une API externe synchrone (comme `localStorage` ou `sessionStorage`) qui peut avoir un léger coût.
- Vous voulez vous assurer qu'une fonction d'initialisation complexe n'est exécutée qu'une seule fois, indépendamment des rendus futurs.
Pour des initialisations simples et rapides (ex: `useState(0)`, `useState('')`, `useState(null)`), l'overhead lié à la création d'une fonction pour l'initialisation paresseuse n'apporte aucun gain notable et peut être omis pour plus de simplicité. L'important est d'identifier les cas où l'optimisation est réellement bénéfique.