Przejdź do głównej zawartości

09. Optymalizacja wydajności React

Optymalizacja wydajności React

React renderuje komponenty za każdym razem gdy zmienia się ich stan lub props. React.memo zapobiega re-renderom gdy props się nie zmieniły. useCallback memoizuje funkcje, useMemo memoizuje kosztowne obliczenia. Ale: przedwczesna optymalizacja jest złem — używaj tylko gdy mierzysz problem.

  1. Kiedy React re-renderuje? — Zmiana state, zmiana props, re-render rodzica
  2. Co to jest memoizacja? — Zapamiętanie wyniku dla tych samych argumentów
  3. React.memo vs useMemo? — memo dla komponentów, useMemo dla wartości
  4. Kiedy NIE używać? — Małe komponenty, rzadko zmieniające się dane — overhead kosztu
  1. Problem zbędnych re-renderów — dlaczego każda zmiana stanu rodzica re-renderuje dziecko
  2. React.memo — HOC owijający komponent, shallow comparison props
  3. useCallback — memoizacja funkcji przekazywanych jako props
  4. useMemo — memoizacja kosztownych obliczeń (filtrowanie, sortowanie dużych list)
  5. Zasada — mierz (Profiler), a potem optymalizuj

Schemat

Drzewo komponentów: rodzic ma stan A i B. Dziecko używa tylko A. Bez memo: zmiana B re-renderuje dziecko. Z memo: zmiana B NIE re-renderuje dziecka — React porównuje props.

Przykład kodu JSX

Lista produktów z filtrem — bez i z useMemo na filtrowanej liście + ekspensywne obliczenie ceny.

  • Kiedy React re-renderuje (diagram)
  • React.memo z prostym przykładem
  • Kiedy NIE optymalizować

Forma: 10 slajdów, 10 minut

Ocena: 3.0
// Bez memo: każdy re-render rodzica = re-render ProductCard
// Nawet gdy product się nie zmienił!
function ProductCard({ product }) {
console.log('Render ProductCard:', product.id);
return <div>{product.name}{product.price}</div>;
}
// Z memo: re-render tylko gdy props product się zmieni
const ProductCard = React.memo(function ProductCard({ product }) {
console.log('Render ProductCard:', product.id);
return <div>{product.name}{product.price}</div>;
});
function ProductList({ products, searchTerm }) {
// Bez useMemo: filtrowanie 10000 produktów przy każdym re-renderze
// Z useMemo: filtrowanie tylko gdy products lub searchTerm się zmieni
const filteredProducts = useMemo(
() => products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
),
[products, searchTerm]
);
return (
<ul>
{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
// Problem: nowa funkcja = nowe referencja = re-render dziecka mimo memo
function Parent() {
const [count, setCount] = useState(0);
// Bez useCallback: nowa funkcja przy każdym renderze rodzica
// Z useCallback: ta sama referencja (chyba że count się zmieni)
const handleDelete = useCallback((id) => {
// logika usuwania
console.log('Usuwam:', id);
}, []); // puste deps = zawsze ta sama funkcja
return <Child onDelete={handleDelete} />;
}
const Child = React.memo(function Child({ onDelete }) {
// Dzięki memo + useCallback — re-render tylko gdy onDelete się zmieni
return <button onClick={() => onDelete(1)}>Usuń</button>;
});

Mierz, nie zgaduj!

Podkreślcie: NIGDY nie optymalizujcie bez dowodów z profilera. Dodawanie memo wszędzie spowalnia aplikację przez overhead porównań props!

Pokaż Profiler

Otwórzcie React DevTools → Profiler → nagrajcie kilka kliknięć. Pokaż klasie flamegraph z kolorowymi komponentami — red = długi render.

Szybkie aplikacje to happy użytkownicy!

Optymalizacja to wyższy poziom React. Po tej prezentacji klasa będzie wiedziała jak identyfikować i naprawiać bottlenecki wydajnościowe!