Stabiliser les propriétés des composants enfants
Objectif : Stabiliser les propriétés fonctionnelles transmises aux composants enfants uniquement lorsque le profilage montre un effet mesurable.
Dans le tableau de bord, une interaction peut recréer des fonctions transmises aux composants qui affichent les dépenses. Même si leur comportement n’a pas changé, ces fonctions sont de nouvelles valeurs pour JavaScript : un enfant qui compare ses propriétés peut alors être considéré comme modifié. Vous allez stabiliser un callback ciblé et vérifier si cela réduit réellement le travail observé, sans ajouter de mémoïsation partout.
Comprendre l’identité d’une fonction
Une fonction créée dans le corps d’un composant est recréée lorsque ce composant s’exécute. Deux fonctions peuvent réaliser la même opération tout en ayant des identités référentielles différentes. Si vous les transmettez comme propriétés à un enfant qui vérifie l’identité de ses propriétés, cette différence peut déclencher du travail supplémentaire.
useCallback conserve une fonction entre les rendus tant que ses dépendances restent identiques. Il ne rend pas la fonction plus rapide et n’empêche pas, à lui seul, un enfant de se rendre : il peut seulement préserver l’identité de la propriété fonctionnelle transmise. Comme établi dans la leçon sur le calcul dérivé, vous partez d’une mesure et vous ne gardez l’optimisation que si elle améliore un cas observé.
Stabiliser le callback transmis à une ligne
Dans cet exemple complet pour src/components/ListeDepenses.tsx, le composant reçoit la liste et l’opération de suppression. Il crée un callback stable qui relaie l’identifiant à cette opération, puis le transmet à chaque ligne. La ligne appelle le callback avec son propre identifiant ; l’opération de suppression reste détenue par la logique appelante, conformément au découpage du projet.
import { useCallback } from "react";
type Depense = {
id: string;
date: string;
description: string;
categorie: string;
montant: number;
};
type ListeDepensesProps = {
depensesFiltrees: Depense[];
supprimerDepense: (id: string) => void;
};
type LigneDepenseProps = {
depense: Depense;
onSupprimer: (id: string) => void;
};
function LigneDepense({ depense, onSupprimer }: LigneDepenseProps) {
return (
<tr>
<td>{depense.date}</td>
<td>{depense.description}</td>
<td>{depense.categorie}</td>
<td>{depense.montant}</td>
<td>
<button
type="button"
onClick={() => onSupprimer(depense.id)}
aria-label={`Supprimer ${depense.description}`}
>
Supprimer
</button>
</td>
</tr>
);
}
export default function ListeDepenses({
depensesFiltrees,
supprimerDepense,
}: ListeDepensesProps) {
const gererSuppression = useCallback(
(id: string) => supprimerDepense(id),
[supprimerDepense],
);
return (
<section id="depenses" className="atelier-liste" aria-labelledby="titre-depenses">
<h2 id="titre-depenses">Dépenses</h2>
{depensesFiltrees.length > 0 ? (
<table className="atelier-liste__tableau">
<caption>Liste des dépenses filtrées</caption>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Description</th>
<th scope="col">Catégorie</th>
<th scope="col">Montant</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{depensesFiltrees.map((depense) => (
<LigneDepense
key={depense.id}
depense={depense}
onSupprimer={gererSuppression}
/>
))}
</tbody>
</table>
) : (
<p className="atelier-etat-vide">Aucune dépense ne correspond aux filtres.</p>
)}
<p className="atelier-annonce" role="status" aria-live="polite">
{depensesFiltrees.length} dépense(s) affichée(s).
</p>
</section>
);
}La dépendance supprimerDepense est nécessaire : si l’opération reçue change, le callback doit être recréé pour appeler la bonne fonction. Si cette propriété change à chaque rendu, useCallback ne peut pas conserver l’identité de gererSuppression. Vérifiez donc aussi la stabilité de la fonction fournie par le parent, au lieu de masquer le problème avec une liste de dépendances vide.
Quand ne pas utiliser useCallback
Si aucun enfant ne compare cette fonction, si le profil ne révèle pas de travail évitable ou si ses dépendances changent à chaque rendu, le hook ajoute de la complexité sans gain démontré. La fonction anonyme présente sur le bouton de la ligne est également recréée à chaque rendu de cette ligne : stabiliser le callback partagé ne supprime pas cette petite fonction d’adaptation. Ne poursuivez cette optimisation que si le profilage identifie précisément cet effet comme pertinent.
À retenir
- Une fonction recréée peut avoir une identité différente même si son comportement reste identique.
- useCallback stabilise une identité tant que ses dépendances ne changent pas ; il ne rend pas une fonction plus rapide.
- La stabilisation n’apporte un bénéfice que si un consommateur de la fonction peut éviter du travail grâce à cette identité stable.
- Déclarez toutes les dépendances nécessaires et mesurez l’effet avant de conserver le hook.
Dans la leçon suivante, vous examinerez comment une frontière de mémoïsation peut exploiter des propriétés stables pour limiter les rendus de lignes indépendantes.