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…