Перш ніж ваші компоненти відобразяться на екрані, їх повинен відрендерити React. Розуміння кроків цього процесу допоможе осмислити перебіг виконання вашого коду та пояснити його поведінку.

You will learn

  • Що таке рендеринг у React
  • Коли і чому React рендерить компонент
  • Кроки відображення компонента на екрані
  • Чому рендеринг не завжди призводить до оновлення DOM

Уявіть, що ваші компоненти — це кухарі на кухні, які створюють смачні страви з інгредієнтів. У такій історії React — це офіціант, який приймає від клієнтів замовлення та видає їм їжу. Цей процес замовлення та видавання UI складається з трьох кроків:

  1. Тригер рендеру (доставлення замовлення гостя на кухню)
  2. Рендер компонента (готування замовлення на кухні)
  3. Коміт у DOM (розміщення замовлення на столі гостя)
  1. React як офіціант у ресторані, що збирає замовлення від клієнтів і передає їх до кухні компонентів (Component Kitchen).
    Тригер
  2. Кухар карток видає React свіжий компонент картки (Card).
    Рендер
  3. React доставляє картку (Card) клієнту на стіл.
    Коміт

Illustrated by Rachel Lee Nabors

Крок 1: Тригер рендеру

Існує дві причини для рендерингу компонента:

  1. Це початковий рендер компонента.
  2. Було оновлено стан компонента (або одного з його предків).

Початковий рендер

Під час запуску застосунку необхідно викликати початковий рендер. Фреймворки та пісочниці іноді приховують цей код, але це насправді виклик createRoot із передачею цільового вузла DOM і потім виклик методу render із вашим компонентом:

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

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

Спробуйте зробити root.render() коментарем — і побачите, що компонент зник!

Повторний рендер під час оновлення стану

Після першого рендеру компонента ви можете збудити (trigger) подальші рендери, оновивши його стан за допомогою функції set. Оновлення стану вашого компонента автоматично додає рендер до черги. (Уявіть відвідувача ресторану, який після першого замовлення замовляє чай, десерт та всяку всячину залежно від стану спраги чи голоду).

  1. React як офіціант у ресторані, що подає клієнту, представленому як постать з курсором замість голови, інтерфейс карти (Card UI). Постать бажає рожеву картку, а не чорну!
    Оновлення стану...
  2. React повертається до кухні компонентів (Component Kitchen) і каже кухареві карток, що потрібна рожева картка (Card).
    ...збуджує...
  3. Кухар карток видає React рожеву картку (Card).
    ...рендер!

Illustrated by Rachel Lee Nabors

Крок 2: React рендерить ваші компоненти

Після тригера рендеру React викликає (calls) ваші компоненти, щоб з’ясувати, що виводити на екран. “Рендеринг” — це коли React викликає ваші компоненти.

  • Під час початкового рендеру React викличе кореневий компонент.
  • Для наступних рендерів React викликатиме функцію компонента, оновлення стану якого власне збудило рендер.

Цей процес є рекурсивним: якщо оновлений компонент повертає якийсь інший компонент, React буде рендерити цей компонент наступним, і якщо цей компонент також щось повертає, він буде рендерити той інший компонент наступним, і так далі. Процес триватиме доти, доки не залишиться вкладених компонентів, і React не знатиме точно, що саме має бути відображено на екрані.

У цьому прикладі React викличе Gallery() й Image() кілька разів:

export default function Gallery() {
  return (
    <section>
      <h1>Скульптури, що надихають</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="'Рід квіткові' ('Floralis Genérica') — Едуардо Каталано (Eduardo Catalano): велетенська металева скульптура квітки зі світловідбивними пелюстками"
    />
  );
}

  • Під час початкового рендеру React створює вузли DOM для <section>, <h1> і трьох тегів <img>.
  • *Під час повторного рендеру React вираховує, які властивості елементів змінилися після попереднього рендеру. Він нічого не робить з цією інформацією до наступного кроку, фази коміту.

Pitfall

Рендеринг завжди має бути чистим обчисленням:

  • Однакові вхідні дані — той самий результат. З урахуванням однакових вхідних даних чиста функція має завжди повертати той самий JSX. (Коли хтось замовляє салат з помідорами, він не повинен отримати салат з цибулею!)
  • Займається лише своєю справою. Воно не повинно змінювати жодних об’єктів або змінних, які існували до рендеру. (Замовлення одного не повинно змінювати замовлення інших.)

Інакше ви можете зіткнутися із заплутаними помилками та непередбачуваною поведінкою зі зростанням складності кодової бази. Під час розробки у суворому режимі (“Strict Mode”) React викликає функцію кожного компонента двічі, що може допомогти виявити помилки, спричинені нечистими функціями.

Deep Dive

Оптимізація продуктивності

Стандартна поведінка, яка полягає у рендерингу всіх компонентів, вкладених в оновлений компонент, не є оптимальною для продуктивності, якщо оновлений компонент знаходиться дуже високо в дереві. Якщо ви зіткнулися з проблемою продуктивності, є кілька варіантів її вирішення, що наведені в розділі “Продуктивність”. Не оптимізуйте передчасно!

Крок 3: React вносить зміни в DOM

Після рендерингу (виклику) ваших компонентів React модифікує DOM.

  • Для початкового рендерингу React використовує API DOM appendChild(), щоб вивести на екран усі новостворені вузли DOM.
  • Для повторних рендерів React застосовує мінімально необхідні операції (обчислені під час рендерингу!), щоб оновити DOM відповідно до результату найсвіжішого рендерингу.

React змінює вузли DOM тільки тоді, коли є різниця між рендерами. Наприклад, ось компонент, який повторно рендериться щосекунди з різними властивостями, що передаються від батьківського компонента. Зверніть увагу, що можна додати текст у <input>, оновивши його value, але текст не зникає, коли компонент рендериться повторно:

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

Це працює, тому що на останньому кроці React оновлює лише вміст <h1> новим time. Він бачить, що <input> з’являється у JSX у тому ж місці, що і минулого разу, тому React не чіпає <input> або його value!

Епілог: Малювання браузера

Коли рендеринг завершено і React оновив DOM, браузер перемальовує екран. Хоча цей процес відомий як “рендеринг браузера”, ми будемо називати його “малюванням” (“painting”), щоб уникнути плутанини в документації.

Картина браузера 'Натюрморт з елементом картки (Сard)'.

Illustrated by Rachel Lee Nabors

Recap

  • Будь-яке оновлення екрану у React-застосунку відбувається у три кроки:
    1. Тригер
    2. Рендер
    3. Коміт
  • Можна скористатися суворим режимом, аби віднайти помилки у компонентах
  • React не змінює DOM, якщо результат рендерингу такий самий, як і минулого разу