Зобразити і зафіксувати

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

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

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

  1. Запуск рендеру (доставка замовлення гостя на кухню)
  2. Рендеринг компонента (підготовка замовлення на кухні)
  3. Фіксація у DOM (розміщення замовлення на столі)

Крок 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 />);
export default function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
    />
  );
}

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

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

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

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

Після того, як ви запускаєте рендер, React викликає ваші компоненти, щоб з'ясувати, що виводити на екран. "Рендерінг" - це виклик React'ом ваших компонентів.

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

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

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

export default function Gallery() {
  return (
    <section>
      <h1>Inspiring Sculptures</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
    />
  );
}
import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(<Gallery />);
img { margin: 0 10px 10px 0; }
  • Під час початкового рендерингу, React створить вузли DOM для <section>, <h1> і трьох тегів <img>.
  • Під час повторного рендерингу React вирахує, які властивості цих елементів, якщо такі були, змінилися з моменту попереднього рендерингу. Він нічого не буде робити з цією інформацією до наступного кроку, фази фіксації.

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

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

В іншому випадку ви можете зіткнутися із заплутаними помилками та непередбачуваною поведінкою у міру зростання складності вашої кодової бази. При розробці у "строгому режимі" React викликає функцію кожного компонента двічі, що може допомогти виявити помилки, спричинені нечистими функціями.

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

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

Крок 3: React фіксує зміни в DOM

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

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

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

export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';

function useTime() {
  const [time, setTime] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return time;
}

export default function App() {
  const time = useTime();
  return (
    <Clock time={time.toLocaleTimeString()} />
  );
}

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

Epilogue: Фарби браузера

Після того, як рендеринг завершено і React оновив DOM, браузер перемалює екран. Хоча цей процес відомий як "рендеринг браузера", ми будемо називати його "малюванням", щоб уникнути плутанини в документації.

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