React - useMemo : Optimisation des Calculs
Quand ton composant fait des calculs coûteux à chaque rendu, useMemo va mémoriser le résultat et ne le recalculer que si les dépendances changent.
Le Problème des Recalculs Inutiles
Calculs Coûteux à Chaque Rendu
function SlowComponent({ items, filter }) {
const [count, setCount] = useState(0)
// ❌ PROBLÈME - Calcul coûteux à CHAQUE rendu !
const expensiveValue = items
.filter(item => item.category === filter)
.reduce((sum, item) => sum + item.price, 0)
console.log('💰 Calcul coûteux exécuté !') // ← Se déclenche même quand count change !
// ❌ PROBLÈME - Nouvel objet à chaque rendu !
const chartConfig = {
type: 'bar',
data: expensiveValue,
options: { responsive: true }
}
// ❌ PROBLÈME - Tableau recalculé à chaque rendu !
const sortedItems = items
.filter(item => item.category === filter)
.sort((a, b) => b.price - a.price)
return (
<div>
<h3>Total: {expensiveValue}€</h3>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<ExpensiveChart config={chartConfig} />
<ItemsList items={sortedItems} />
</div>
)
}
// Ces composants re-render même si leurs props n'ont pas vraiment changé !
const ExpensiveChart = memo(function ExpensiveChart({ config }) {
console.log('📊 ExpensiveChart re-rendu') // ← Toujours appelé car config est toujours un nouvel objet !
return <div>Chart avec config: {JSON.stringify(config)}</div>
})
const ItemsList = memo(function ItemsList({ items }) {
console.log('📝 ItemsList re-rendu') // ← Toujours appelé car items est toujours un nouveau tableau !
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name} - {item.price}€</li>
))}
</ul>
)
})
useMemo à la Rescousse
Syntaxe et Usage Basique
import { useMemo } from 'react'
function OptimizedComponent({ items, filter }) {
const [count, setCount] = useState(0)
// ✅ SOLUTION - Calcul seulement si items ou filter changent
const expensiveValue = useMemo(() => {
console.log('💰 Calcul coûteux exécuté') // ← Seulement si nécessaire !
return items
.filter(item => item.category === filter)
.reduce((sum, item) => sum + item.price, 0)
}, [items, filter]) // ← Dépendances
// ✅ Objet complexe mémorisé
const chartConfig = useMemo(() => {
console.log('📊 Configuration chart générée')
return {
type: 'bar',
data: expensiveValue,
options: { responsive: true },
theme: 'modern'
}
}, [expensiveValue])
// ✅ Données filtrées et triées mémorisées
const sortedItems = useMemo(() => {
console.log('🔍 Filtrage et tri des items')
return items
.filter(item => item.category === filter)
.sort((a, b) => b.price - a.price) // Tri par prix décroissant
}, [items, filter])
return (
<div>
<h3>Total: {expensiveValue}€</h3>
<p>Count: {count} (ne déclenche plus le calcul !)</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<ExpensiveChart config={chartConfig} />
<ItemsList items={sortedItems} />
</div>
)
}
// Maintenant ces composants ne re-render que si leurs props changent vraiment !
const ExpensiveChart = memo(function ExpensiveChart({ config }) {
console.log('📊 ExpensiveChart re-rendu') // ← Seulement si config change vraiment
return <div>Chart avec config: {JSON.stringify(config)}</div>
})
const ItemsList = memo(function ItemsList({ items }) {
console.log('📝 ItemsList re-rendu') // ← Seulement si items change vraiment
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name} - {item.price}€</li>
))}
</ul>
)
})