Renderowanie i aktualizowanie

Zanim twoje komponenty zostaną wyświetlone na ekranie, muszą zostać wyrenderowane przez Reacta. Zrozumienie tego procesu pomoże ci zrozumieć, jak wykonuje się twój kod i wyjaśni jego zachowanie.

W tej sekcji dowiesz się

  • Czym jest renderowanie w Reakcie
  • Kiedy i dlaczego React renderuje komponenty
  • Kroki związane z wyświetlaniem komponentów na ekranie
  • Dlaczego renderowanie nie zawsze powoduje aktualizację drzewa DOM

Wyobraź sobie, że twoje komponenty to kucharze w kuchni, którzy przygotowują smaczne dania z dostępnych składników. W tej sytuacji React jest kelnerem, który przyjmuje zamówienia od klientów i przynosi im zamówione potrawy. Ten proces zgłaszania i obsługi interfejsu użytkownika składa się z trzech kroków:

  1. Wywołanie (ang. triggering) renderowania (przekazanie zamówienia od gościa do kuchni)
  2. Renderowanie (ang. rendering) komponentu (przygotowanie zamówienia w kuchni)
  3. Aktualizowanie (ang. committing) drzewa DOM (umieszczenie zamówienia na stole)
  1. React jako kelner w restauracji, pobierający zamówienia od użytkowników i dostarczający je do Kuchni Komponentów.
    Wywołanie
  2. Kucharz komponentu Card przekazuje Reactowi świeży komponent Card.
    Renderowanie
  3. React dostarcza komponent Card użytkownikowi do jego stołu.
    Aktualizowanie

Autor ilustracji Rachel Lee Nabors

Krok 1: Wywołanie renderowania

Istnieją dwa powody, dla których komponent może zostać wyrenderowany:

  1. To jest początkowe renderowanie komponentu.
  2. Stan komponentu (lub jednego z jego rodziców) został zaktualizowany.

Początkowe renderowanie

Przy uruchamianiu swojej aplikacji, musisz wywołać początkowe renderowanie. Frameworki i piaskownice czasem ukrywają ten kod, ale jest on wywoływany poprzez wywołanie funkcji createRoot z docelowym węzłem drzewa DOM, a następnie wywołanie na nim metody render z twoim komponentem:

import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(<Image />);

Spróbuj zakomentować wywołanie root.render() i zauważ, że komponent znika!

Przerenderowania po aktualizacji stanu

Po początkowym renderowaniu komponentu, możesz wywołać kolejne przerenderowania poprzez aktualizację jego stanu za pomocą funkcji set. Aktualizacja stanu komponentu automatycznie dodaje renderowanie do kolejki. (Możesz to sobie wyobrazić jako gościa restauracji zamawiającego herbatę, deser i wszelkiego rodzaju rzeczy już po złożeniu pierwszego zamówienia, w zależności od stanu jego pragnienia lub głodu.)

  1. React jako kelner w restauracji, serwujący interfejs użytkownika Card użytkownikowi, przedstawionym jako klient z kursorem jako głową. Klient mówi, że chce komponent Card w kolorze różowym, a nie czarnym!
    Aktualizacja stanu...
  2. React wraca do Kuchni Komponentów i mówi Kucharzowi Komponentu Card, że potrzebuje komponent Card w kolorze różowym.
    ...wywołuje...
  3. Kucharz Komponentu Card dostarcza Reactowi komponent Card w kolorze różowym.
    ...renderowanie!

Autor ilustracji Rachel Lee Nabors

Krok 2: React renderuje twoje komponenty

Po wywołaniu renderowania, React wywołuje twoje komponenty, aby ustalić, co wyświetlić na ekranie. “Renderowanie” oznacza wywołanie twoich komponentów przez Reacta.

  • Podczas początkowego renderowania, React wywoła główny komponent.
  • Podczas kolejnych renderowań, React wywoła funkcję komponentu, którego aktualizacja stanu wywołała renderowanie.

Proces ten jest rekurencyjny: jeśli zaktualizowany komponent zwraca inny komponent, React następnie wyrenderuje ten komponent, a jeśli ten komponent również coś zwraca, wyrenderuje ten komponent, i tak dalej. Proces będzie kontynuowany, aż nie będzie więcej zagnieżdżonych komponentów i React będzie dokładnie wiedział, co powinno być wyświetlane na ekranie.

W poniższym przykładzie React wywoła Gallery() i Image() kilkukrotnie:

export default function Gallery() {
  return (
    <section>
      <h1>Inspirujące rzeźby</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="Rzeźba 'Floralis Genérica' wykonana przez Eduardo Catalano: ogromny metalowy kwiat z zwierciadlanymi płatkami"
    />
  );
}

  • Podczas początkowego renderowania, React utworzy węzły DOM dla znaczników <section>, <h1> i trzech znaczników <img>.
  • Podczas ponownego renderowania, React obliczy, które z ich właściwości, jeśli jakiekolwiek, zostały zmienione od poprzedniego renderowania. Nic nie zrobi z tą informacją aż do następnego kroku, czyli fazy aktualizacji.

Zwróć uwagę

Renderowanie zawsze musi być czystym obliczaniem:

  • Takie same wejścia, taki sam wynik. Dla tych samych danych wejściowych, komponent powinien zawsze zwracać ten sam JSX - kiedy ktoś zamawia sałatkę z pomidorami, nie powinien otrzymać sałatki z cebulą!
  • Dbanie o swoje własne sprawy. Komponent nie powinien zmieniać żadnych obiektów ani zmiennych, które już istniały przed renderowaniem - jedno zamówienie nie powinno móc zmieniać zamówienia kogoś innego.

W przeciwnym razie możesz napotkać trudne do zrozumienia błędy i nieprzewidywalne zachowanie w miarę wzrostu złożoności kodu. Podczas pracy w “trybie rygorystycznym” (ang. Strict Mode) React wywołuje funkcję każdego komponentu dwa razy, co może pomóc w wykryciu błędów spowodowanych przez nieczyste funkcje.

Dla dociekliwych

Optymalizacja wydajności

Domyślne zachowanie polegające na renderowaniu wszystkich komponentów zagnieżdżonych w zaktualizowanym komponencie nie jest optymalne pod względem wydajności, jeśli zaktualizowany komponent znajduje się bardzo wysoko w drzewie komponentów. Jeśli napotkasz problemy z wydajnością, istnieje kilka możliwych rozwiązań opisanych w sekcji Wydajność. Nie optymalizuj przedwcześnie!

Krok 3: React aktualizuje zmiany w drzewie DOM

Po wyrenderowaniu (wywołaniu) twoich komponentów, React zmodyfikuje drzewo DOM.

  • Podczas początkowego renderowania, React użyje API drzewa DOM o nazwie appendChild(), aby umieścić na ekranie wszystkie węzły drzewa DOM, które utworzył.
  • Podczas przerenderowań, React zastosuje minimum niezbędnych operacji (obliczonych podczas renderowania!), aby dopasować drzewo DOM do wyniku najnowszego renderowania.

React zmienia węzły drzewa DOM tylko wtedy, gdy występuje różnica pomiędzy renderowaniami. Na przykład, oto komponent, który przerenderowuje się co sekundę z różnymi właściwościami przekazywanymi z jego rodzica. Zauważ, że możesz dodać tekst do elementu <input>, aktualizując jego atrybut value, ale tekst nie znika, gdy komponent renderuje się ponownie:

export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}

To działa, ponieważ podczas ostatniego kroku React aktualizuje tylko zawartość znacznika <h1> nową wartością time. Widzi on, że znacznik <input> pojawia się w JSXie w tym samym miejscu co ostatnim razem, więc React nie zmienia tego znacznika ani jego atrybutu value!

Epilog: Malowanie przez przeglądarkę

Po zakończeniu renderowania i zaktualizowaniu drzewa DOM przez Reacta, przeglądarka przemaluje ekran. Chociaż proces ten jest znany jako “renderowanie przez przeglądarkę”, będziemy się odnosić do niego jako “malowanie”, aby uniknąć nieporozumień w dokumentacji.

Przeglądarka malująca 'martwą naturę z elementem komponentu Card'.

Autor ilustracji Rachel Lee Nabors

Powtórka

  • Każda aktualizacja ekranu w aplikacji reactowej odbywa się w trzech krokach:
    1. Wywołanie
    2. Renderowanie
    3. Aktualizacja
  • Możesz użyć trybu rygorystycznego, aby znaleźć błędy w swoich komponentach
  • React nie modyfikuje drzewa DOM, jeśli wynik renderowania jest taki sam jak poprzednio