Poznajemy czym jest memo w React i do czego służy keszowanie RFC. Upewniamy się, że rozumiemy jak działa komunikacja i przepływ informacji w React oraz kiedy występuje re-renderowanie.
Ok, komponent jest re-renderowany (rozumiem przez to renderowanie bez moutowania) kiedy:
- Jego stan ulega zmianie
- Jego kontekst ulega zmianie
- Jego rodzic jest re-renderowany
Pewnym złym rozumieniem sprawy, jest myślenie, że „renderowany jest, gdy jego propsy się zmienią”. Tak nie jest, propsy się nie zmieniają:
- Jednostronny przepływ danych sprawia, że propsy idą od rodzica do dziecka, dziecko może je zmieniać (to bez sensu), ale w dwie strony to nie działa
- Propsy „zmieniają się” gdy komponent-rodzic jest re-renderowany a wraz z nim dzieci, które dostają nowe propsy – np. prop value dziecka jest jednocześnie stanem rodzica, ten stan się zmienia, mamy re-render, także re-render dzieci, dzieci dostają nową wartość propsa
Ok, a co można w React memoizować/keszować? To, co jest tworzone od zera, choć nie zawsze musi:
- Wyniki komputacji/przypisań do zmiennej wewnątrz RFC – służy do tego useMemo
- Definicje funkcji wewnątrz RFC, aby nie były za każdym razem tworzone od zera – służy do tego useCallback
- Sam RFC, aby nie był tworzony od zera, gdy mamy re-render rodzica – to właśnie memo
Składnia memo wygląda tak:
import { memo } from 'react';
const SomeComponent = memo(function SomeComponent(props) {
// ...
});
I to oznacza, że RFC SomeComponent nie będzie tworzony od zera za każdym razem, gdy jego rodzic będzie re-renderowany – chyba, że propsy przekazywane przez tego rodzica ulegną zmianie.
Proszę, oto przykład RFC Greeting, które jest memoizowane:
const Greeting = memo(function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
});
export default Greeting;
Teraz re-render rodzica wywoła re-render Greeting wtedy i tylko wtedy, gdy wartość name przekazywana jako prop ulegnie jakiejś zmianie:
import { memo, useState } from 'react';
export default function MyApp() {
const [name, setName] = useState('');
const [address, setAddress] = useState('');
return (
<>
<label>
Name{': '}
<input value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Address{': '}
<input value={address} onChange={e => setAddress(e.target.value)} />
</label>
<Greeting name={name} />
</>
);
}
Normalnie każda zmiania stanu rodzica (w tym zmiania address) wywoływała by re-render rodzica i wszystkich jego dzieci. Greeting jest jednak keszowany i zmiania address a co za tym idzie re-render MyApp nie wywołuje re-renderu Greeting.
Re-render Greeting zachodzi tylko wtedy, gdy zmieni się name, rodzic zostanie re-renderowany i nowe name jako prop zostanie przekazane do Greeting.
Więcej Reacta niedługo…