Главная / Статьи

Объяснение рендеринга в React: обновление состояния и отображение на пикселях экрана

Как фазы рендеринга, согласования и сохранения состояния в React связаны с процессом рендеринга в браузере: отрисовка лейаута, окраска и композиция изображения — от обновления состояния до фактических пикселей на экране.

Издатель: REACTAPP.TOP17 мин чтения4015 слов

Источники: Rendering Lifecycle in React: From State Update to Pixels on the Screen

При работе с React часто можно услышать фразу «компонент перерисован». Но что на самом деле означает процесс рендеринга?

Здесь возникает несколько естественных вопросов. Передаёт ли React обновления в DOM сразу при изменении состояния? Приводит ли изменение состояния к перерисовке каждого компонента? И когда React перерисовывает что-то, означает ли это, что браузер немедленно перерисовывает всю страницу? Оказывается, ни один из этих вариантов не верен.

Чтобы по-настоящему понять жизненный цикл рендеринга, необходимо разобраться в двух отдельных системах, работающих вместе:

Собственный жизненный цикл рендеринга React и пайплайн рендеринга браузера.

Как только вы поймете, как связаны эти две системы, такие понятия, как примирение состояний, фаза коммитов, useEffect, useLayoutEffect, повторная отрисовка и мемоизация, перестанут казаться чем-то волшебным и станут иметь смысл.

1. Общая картина

На высоком уровне процесс выглядит следующим образом:

User Interaction
      │
      ▼
State / Props Change
      │
      ▼
┌──────────────────────┐
│     React Render     │
│                      │
│  Component executes  │
│  New UI is returned  │
└──────────┬───────────┘
           │
           ▼
     Reconciliation
           │
           ▼
┌──────────────────────┐
│     Commit Phase     │
│                      │
│  Update real DOM     │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────────┐
│    Browser Rendering     │
│                          │
│ Style → Layout → Paint   │
│           → Composite    │
└────────────┬─────────────┘
             │
             ▼
           Screen

Главный вывод заключается в следующем:

React определяет, как должен выглядеть интерфейс, и какие изменения необходимы в DOM. Затем браузер преобразует эти изменения в реальные пиксели на экране.

2. Что означает «отрисовка»?

Термин отрисовка используется в нескольких значениях.

В контексте React отрисовка обычно означает следующее:

React вызывает функцию компонента и определяет, каким должен быть результативный интерфейс.

Рассмотрим этот компонент:

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button>
      Count: {count}
    </button>
  );
}

Когда React его отрисовывает, выполняется тело функции:

Counter()
   ↓
returns React element
   ↓
<button>Count: 0</button>

Это не означает, что браузер уже отрисовал кнопку на экране.

Прежде чем произойдёт что-либо визуальное, React всё ещё должен решить, действительно ли необходимо изменять DOM.

Короче говоря:

React Render
     ≠
DOM Update
     ≠
Browser Paint

Отрисовка и обновление DOM — это две разные операции.

3. Жизненный цикл отрисовки браузера

Прежде чем углубляться в внутренности React, полезно понять, что делает сам браузер.

Представьте, что у вас есть такая разметка:

<h1 class="title">
  Hello World
</h1>

В сочетании с этим стилем:

.title {
  color: red;
  font-size: 30px;
}

Прежде чем этот контент вообще появится на экране, браузер выполняет ряд внутренних шагов.

Упрощённая версия этой последовательности выглядит так:

HTML
 ↓
DOM
 ↓
CSS
 ↓
CSSOM
 ↓
Render Tree
 ↓
Layout
 ↓
Paint
 ↓
Composite
 ↓
Screen

Давайте рассмотрим каждый этап.

4. HTML → DOM

Браузер читает HTML и создаёт DOM, что является сокращением от Document Object Model.

Исходя из этого маркирования:

<body>
  <h1>Hello World</h1>
  <p>Welcome</p>
</body>

Браузер формирует древовидную структуру примерно вот так:

Document
   │
   └── body
       ├── h1
       │   └── "Hello World"
       │
       └── p
           └── "Welcome"

Затем JavaScript может напрямую запрашивать и изменять эту структуру:

document.querySelector("h1");

По сути, DOM отвечает на один вопрос:

Какие элементы существуют на странице?

5. CSS → CSSOM

Помимо DOM, браузер также парсит ваш CSS и создаёт CSSOM, то есть CSS Object Model.

Например:

h1 {
  color: red;
  font-size: 30px;
}

На основе этого браузер определяет, какие стили применяются к каким элементам.

Концептуально это выглядит так:

h1
 ├── color: red
 └── font-size: 30px

CSSOM отвечает на другой вопрос:

Как должен визуально выглядеть каждый элемент?

6. DOM + CSSOM → Render Tree

Далее браузер объединяет DOM и CSSOM для создания дерева отрисовки.

Существует нюанс, который стоит отметить:

Не каждый узел в DOM оказывается представленным в дереве отрисовки.

Например, возьмем такой маркап:

<div class="hidden">
  Hello
</div>

В сочетании с этим правилом:

.hidden {
  display: none;
}

Элемент div присутствует в DOM:

DOM
 └── div

Но поскольку он содержит:

display: none;

он полностью исключается из визуального дерева отрисовки.

7. Макетирование

Как только браузер узнает, какие элементы необходимо отобразить, он определяет их геометрию.

Это включает расчет таких параметров, как:

  • Ширина
  • Высота
  • Позиция
  • Отступы
  • Внутренние отступы
  • Координаты
  • Способ пространственного расположения элементов друг относительно друга

В качестве примера возьмем это правило:

.box {
  width: 200px;
  height: 100px;
  margin-left: 50px;
}

Браузер должен вычислить конкретные значения в соответствии с этими принципами:

x      = 50px
y      = ...
width  = 200px
height = 100px

Этот этап вычислений называется Layout, его также часто называют Reflow.

8. Paint

После завершения этапа Layout у браузера есть координаты для каждого видимого элемента.

Следующий шаг — отрисовка того, как на самом деле выглядят эти элементы.

Рассмотрим следующее стилизование:

.box {
  background: blue;
  color: white;
  border: 2px solid black;
}

Процесс Paint включает в себя такие элементы:

  • Фон
  • Текст
  • Границы
  • Тени
  • Изображения
  • Другие визуальные элементы

В следующем порядке:

Layout
  ↓
Paint
  ↓
Pixels

9. Composite

Современные браузеры могут разделить страницу на несколько отдельных слоев.

Например:

Layer 1 → Page background
Layer 2 → Header
Layer 3 → Content
Layer 4 → Modal

Затем механизм композиции браузера объединяет все эти слои в один изображение, которое отображается на экране.

Некоторые свойства CSS, такие как:

transform
opacity

Как правило, его можно анимировать с помощью композитинга, тем самым избегая более сложной работы по формированию лейаута, требуемой свойствами, влияющими на геометрию.

Именно поэтому анимация свойства transform обычно считается предпочтительным подходом.

10. Жизненный цикл отрисовки в React

Теперь, когда мы рассмотрели сторону браузера, давайте перейдём к тому, как React обрабатывает отрисовку.

Полезная модель позволяет разделить этот процесс на четыре этапа:

1. Trigger
2. Render
3. Reconciliation
4. Commit
5. Browser Rendering

Давайте рассмотрим каждый из них по отдельности.

11. Этап 1 — Триггер

Прежде чем что-либо произойдёт, React нужен сигнал, указывающий на необходимость действия:

"Возможно, требуется обновление интерфейса пользователя."

Этот сигнал называется триггером отрисовки.

Типичные триггеры включают:

1. Обновление состояния: setCount(10);

2. Изменение параметров: <Child count={count} />

Когда родительский компонент перерисовывается с новым значением для count, его дочерние компоненты могут в результате получить новые параметры.

3. Обновление контекста: useContext(MyContext);

Каждый раз, когда меняется значение, зачитываемое из контекста, компоненты, которые им пользуются, могут потребоваться к обновлению.

4. Обновление внешнего хранилища: библиотеки и API также могут запускать обновления в React.

Примеры включают:

Redux
Zustand
useSyncExternalStore

Первая загрузка приложения также считается триггером.

12. Этап 2 — Рендеринг

Как только React обнаруживает обновление, он переходит к этапу рендеринга, где запускает компоненты для определения нового интерфейса.

Возьмем этот пример:

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

В начале count равен 0. React вызывает Counter() и получает:

<button>
  Count: 0
</button>

После нажатия кнопки и выполнения этого кода:

setCount(c => c + 1);

count становится 1, и React перезапускает компонент, в результате чего:

<button>
  Count: 1
</button>

13. Важно: отрисовка не означает обновление DOM

Это критически важное различие в модели React.

Когда React выполняет эту функцию:

function Counter() {
  console.log("Counter rendered");

  return <button>Count: 1</button>;
}

он просто вычисляет, какими должны быть элементы интерфейса. Это не означает автоматически, что DOM изменился или что браузер что-то перерисовал.

Фактическая последовательность выглядит так:

Component executes
       ↓
React creates new element representation
       ↓
Reconciliation
       ↓
Determine required DOM changes
       ↓
Commit changes

Это приводит к важному правилу:

Отрисовка компонента не автоматически приводит к обновлению DOM.

14. Этап 3 — согласование

На этом этапе React сравнивает старый и новый интерфейсы, чтобы определить, в чём именно произошли изменения.

Предположим, что предыдущая структура выглядела так:

button
 └── "Count: 0"

А новая структура выглядит так:

button
 └── "Count: 1"

React может определить:

button → same
text   → changed

Поэтому ему достаточно изменить только ту часть DOM, которая действительно изменилась:

button.textContent = "Count: 1";

Главная цель — избежать демонтажа и повторной сборки всего дерева DOM, когда изменилась лишь небольшая его часть.

15. Ключи и согласование

Ключи имеют особое значение, когда React работает с списками.

Например:

items.map(item => (
  <li key={item.id}>
    {item.name}
  </li>
))

React использует ключи для отслеживания того, какой элемент является каким в процессе перерисовки. Предположим, у нас есть:

Before:

A
B
C

и в итоге получаем:

After:

A
C

При наличии стабильных ключей React может правильно сделать выводы:

A → same
B → removed
C → same

Это предоставляет React гораздо более надежную информацию для работы в процессе согласования. Избегайте использования индексов массива в качестве ключей, если элементы списка могут быть переставлены, добавлены или удалены.

16. Этап 4 — Фиксация изменений

Как только React определяет, что нужно изменить, он переходит к этапу коммита, на котором фактически применяет эти изменения к реальному DOM.

Представьте элемент кнопки, надпись на котором меняется с нуля на один по окончании этого этапа.

На этом этапе внутренняя структура React и динамический DOM наконец совпадают.

Этап коммита — это отдельный шаг по сравнению с этапом отрисовки:

Render Phase
    ↓
Calculate what should change

Commit Phase
    ↓
Actually apply DOM changes

17. Этап отрисовки против этапа коммита

Понимание этого разделения — один из наиболее часто задаваемых вопросов на собеседованиях.

Этап отрисовки

На этом этапе React:

  • Запускает функции компонентов
  • Определяет следующую версию интерфейса
  • Выполняет синхронизацию
  • Определяет, что нужно изменить
Component
   ↓
React elements
   ↓
Reconciliation

Этап коммита

На этом этапе React:

  • Применяются фактические мутации DOM
  • Обновляются все ссылки
  • Выполняются эффекты макетирования, связанные с этим обновлением
React result
    ↓
Real DOM

Вот упрощенная схема процесса:

             React
               │
               ▼
        ┌──────────────┐
        │ Render Phase │
        └──────┬───────┘
               │
               ▼
        ┌───────────────┐
        │ Commit Phase  │
        └──────┬────────┘
               │
               ▼
            Real DOM

18. Что происходит после коммита?

После завершения этапа коммита браузер берет управление на себя и замечает, что DOM изменился.

В зависимости от того, что изменилось, браузеру может потребоваться пройти через несколько этапов:

DOM Changes
    ↓
Style Calculation
    ↓
Layout
    ↓
Paint
    ↓
Composite

Не каждое изменение требует всех этих этапов для выполнения значимой работы — например, иногда достаточно лишь обновления отображения без полного пересчета макета.

Именно в этот момент собственный цикл жизни React передает управление описанной ранее системе отрисовки браузера.

19. useLayoutEffect

useLayoutEffect вызывается после того, как React сохранит изменения в DOM, но до того, как браузер сможет отрисовать новый вид элементов.

Последовательность действий выглядит следующим образом:

React Render
     ↓
Commit
     ↓
DOM updated
     ↓
useLayoutEffect
     ↓
Browser Paint
function Tooltip() {
  const ref = useRef(null);

  useLayoutEffect(() => {
    const rect = ref.current.getBoundingClientRect();

    console.log(rect.width);
  }, []);

  return (
    <div ref={ref}>
      Tooltip
    </div>
  );
}

Этот хук — подходящий инструмент, когда вам необходимо:

  • Измерить элементы в DOM
  • Загрузить текущие значения макета
  • Применить визуальные корректировки синхронно, до того как пользователь что-либо увидит

Одно предупреждение: если работа внутри useLayoutEffect является тяжелой, она может замедлить отрисовку и сделать интерфейс медленным.

20. useEffect

useEffect, в отличие от него, предназначен для операций, которые не должны блокировать процесс отрисовки браузером.

useEffect(() => {
  fetch("/api/products");
}, []);
Render
   ↓
Commit
   ↓
Browser Paint
   ↓
useEffect

Фактические детали планирования немного сложнее, чем описано здесь, но основное практическое правило заключается в следующем:

Используйте useLayoutEffect только тогда, когда что-то должно произойти до отрисовки браузером; для всего, что может выполниться после того, как интерфейс уже стал видимым, используйте useEffect.

21. Полный пример

import { useEffect, useLayoutEffect, useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  console.log("Render");

  useLayoutEffect(() => {
    console.log("Layout Effect");
  });

  useEffect(() => {
    console.log("Effect");
  });

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

Концептуально последовательность событий выглядит так:

User clicks button
        ↓
setCount()
        ↓
State update scheduled
        ↓
Counter() executes
        ↓
"Render"
        ↓
React reconciles
        ↓
React commits DOM update
        ↓
"Layout Effect"
        ↓
Browser paints updated UI
        ↓
"Effect"

22. Приводит ли перерисовка родительского элемента к перерисовке дочернего?

Возьмем этот пример:

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>
        {count}
      </button>

      <Child />
    </>
  );
}

Когда обновляется count:

Parent renders
       ↓
Child is encountered
       ↓
Child may render as part of reconciliation

So,

Parent state update
      ↓
Parent render
      ↓
Child can render

Такое поведение является одной из причин существования React.memo в качестве инструмента оптимизации.

23. React.memo

Предположим, у вас есть:

const Child = React.memo(function Child() {
  console.log("Child rendered");

  return <p>Hello</p>;
});

Если родительский элемент перерисовывается, но пропсы, передаваемые дочернему элементу, остаются идентичными по ссылке, React может полностью пропустить перерисовку этого мемоизированного дочернего элемента.

Вот как это выглядит визуально:

Parent update
     ↓
Parent renders
     ↓
React.memo checks Child props
     ↓
Props unchanged
     ↓
Skip Child render

Однако имейте в виду:

React.memo предоставляет способ улучшения производительности — он не гарантирует, что компонент больше никогда не будет отрисован.

Изменения контекста, внутреннее состояние и другие факторы могут всё равно заставить его обновиться, независимо от мемоизации.

24. useMemo и useCallback

Эти два хука часто неправильно понимаются.

useMemo: Он мемоизирует вычисленное значение.

const result = useMemo(() => expensiveCalculation(data), [data]);

useCallback: Он мемоизирует ссылку на функцию.

const handleClick = useCallback(() => {
  doSomething(id);
}, [id]);

Оба действительно полезны в ситуациях, когда равенство ссылок важно для избежания повторной обработки — особенно при передаче пропсов мемоизированным дочерним компонентам.

Тем не менее их использование повсюду по умолчанию обычно увеличивает сложность кода без реальной выгоды в производительности.

25. Отрисовка в React — это не то же самое, что отображение в браузере

Этот момент стоит отметить прямо.

Представьте следующий компонент:

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>
        {count}
      </button>

      <p>Hello</p>
    </div>
  );
}

Когда меняется count:

App renders
     ↓
New React tree created
     ↓
Reconciliation
     ↓
Only count text differs
     ↓
Commit small DOM update
     ↓
Browser rendering

React не обязательно будет полностью пересоздавать эту структуру заново:

<div>
  <button>...</button>
  <p>Hello</p>
</div>

26. Виртуальный DOM — правильное понимание концепции

Часто можно встретить утверждение, что React использует виртуальный DOM для ускорения работы браузера. Это слишком упрощённое объяснение.

На самом деле React хранит в памяти внутренний описатель интерфейса и с помощью процесса сопоставления определяет, что изменилось.

Сам браузер не имеет понятия виртуального DOM. Он работает только с такими элементами, как HTML, DOM, CSS и, в конечном итоге, пиксели.

Концептуально процесс выглядит следующим образом:

React Component
      ↓
React Element Tree
      ↓
Reconciliation
      ↓
DOM Mutations
      ↓
Browser
      ↓
Pixels

Виртуальный DOM никогда не является тем, что браузер фактически отрисовывает.

27. Перерисовка против обновления отрисовки

Это два разных понятия, которые не следует смешивать.

Перерисовка в React

Это происходит, когда функция компонента выполняется снова — по сути, вызывается Component().

Обновление отрисовки в браузере

Это происходит, когда браузер перерисовывает пиксели на экране из-за изменения какого-либо визуального свойства — это этап Paint.

Поэтому имейте в виду: перерисовка в React не эквивалентна обновлению отрисовки в браузере.

Вполне возможно, что компонент будет перерисован снова, но структура DOM останется прежней. Напротив, браузер может обновить отрисовку по причинам, не связанным с отрисовкой какого-либо компонента React.

28. Перераспределение элементов против обновления отрисовки

Возьмем в пример следующий код: element.style.width = "500px";

Изменение ширины элемента обычно приводит к пересчету макета:

Style
 ↓
Layout
 ↓
Paint
 ↓
Composite

Теперь сравним это с следующим кодом: element.style.backgroundColor = "red";

В этом случае браузер, как правило, не нуждается в повторном выполнении вычислений геометрии:

Style
 ↓
Paint
 ↓
Composite

А при некоторых изменениях преобразований или прозрачности браузеру может потребоваться лишь:

Composite

Понимание этой разницы имеет большое значение при оптимизации анимаций или интерфейсов с высокими требованиями к отрисовке.

29. Применение этого принципа в реальном сценарии React

Представьте типичную страницу товара в интернет-магазине, структурированную следующим образом:

Product Page
 ├── Navbar
 ├── Product Gallery
 ├── Product Information
 ├── Add To Cart
 ├── Reviews
 └── Recommendations

Предположим, пользователь нажимает кнопку Добавить в корзину, что приводит к обновлению количества товаров в корзине.

Концептуально последовательность событий может выглядеть так:

User clicks "Add to Cart"
           ↓
Cart state updates
           ↓
React schedules update
           ↓
Relevant components render
           ↓
React reconciles new tree
           ↓
Only required DOM changes are committed
           ↓
Browser performs necessary rendering work
           ↓
Updated cart count appears

В хорошо структурированном приложении обновление количества элементов корзины не должно принуждать к ненужной пересборке всего интерфейса. Именно поэтому решения, касающиеся границ компонентов, местоположения состояния, методов мемоизации и общей архитектуры отрисовки, имеют огромное значение.

30. Когда дорогостоящая отрисовка становится проблемой производительности

function ProductList({ products }) {
  return products.map(product => (
    <ExpensiveProductCard
      key={product.id}
      product={product}
    />
  ));
}

Предположим, что этот список отображает 10 000 товаров, и какое-то не связанное с ним изменение состояния приводит к лишней работе в большом количестве компонентов — производительность может существенно ухудшиться.

К распространённым методам снижения негативного воздействия относятся:

React.memo
      +
Stable props
      +
useMemo/useCallback where justified
      +
Virtualization
      +
Pagination
      +
Code splitting

Для особенно больших списков виртуализация обычно оказывается особенно эффективной:

10000 items
     ↓
Only visible ~20-50 items rendered

31. Как отрисовка взаимодействует с одновременной работой React

В более новых версиях React появились более продвинутые возможности планирования. React может присваивать приоритет различным обновлениям и, когда это целесообразно, приостанавливать или откладывать работу по отрисовке.

Основная концепция, которую следует помнить:

Render work
    ↓
Can be scheduled
    ↓
Can be interrupted in modern React
    ↓
Eventually committed

Именно по этой причине побочные эффекты не должны находиться внутри логики отрисовки компонента:

function Component() {
  fetch("/api/data"); // ❌ Side effect during render

  return <div>Hello</div>;
}

Вместо этого используйте хук effect или подходящий шаблон получения данных. В идеале фаза отрисовки должна оставаться чистой.

32. Аргументы в пользу чистой отрисовки

Концептуально компонент React должен вести себя как чистая функция:

UI = f(props, state)

При одинаковых параметрах и состоянии он всегда должен отображать один и тот же интерфейс. Например:

function Greeting({ name }) {
  return <h1>Hello {name}</h1>;
}

Такое поведение предсказуемо и безопасно.

В отличие от этого, выполнение подобных действий внутри функции отрисовки является проблематичным:

function Component() {
  localStorage.setItem("x", "1"); // ❌
  fetch("/api");                  // ❌

  return <div>Hello</div>;
}

Это вносит побочные эффекты в этап, который React может выполнить более одного раза или запланировать неожиданным образом.

Побочные эффекты должны находиться в другом месте — внутри функций эффектов, обработчиков событий или механизмов получения данных, предоставляемых вашей фреймворк-средой.

33. Жизненный цикл отрисовки — полная картина

                    USER ACTION
                         │
                         ▼
               State / Props Update
                         │
                         ▼
                React Schedules Work
                         │
                         ▼
                 ┌──────────────┐
                 │    RENDER    │
                 │              │
                 │ Components   │
                 │ execute      │
                 └──────┬───────┘
                        │
                        ▼
                 Reconciliation
                        │
                        ▼
                 ┌──────────────┐
                 │    COMMIT    │
                 │              │
                 │ DOM mutation │
                 └──────┬───────┘
                        │
                        ▼
                 useLayoutEffect
                        │
                        ▼
              Browser Rendering
                        │
                        ▼
                 Style Calculation
                        │
                        ▼
                     Layout
                        │
                        ▼
                      Paint
                        │
                        ▼
                   Composite
                        │
                        ▼
                      SCREEN
                        │
                        ▼
                   useEffect

Распространённые вопросы на собеседовании

В1. Приводит ли обновление состояния к немедленному обновлению DOM?

Нет. Обновление состояния лишь планирует выполнение действий React. React отрисовывает компонент, сравнивает полученный результат с предыдущим и только затем применяет те изменения DOM, которые действительно необходимы.

setState
  ↓
Render
  ↓
Reconciliation
  ↓
Commit
  ↓
DOM

В2. Приводит ли каждая отрисовка к обновлению DOM?

Нет. React может отрисовать компонент и при этом сделать вывод, что полученное описание интерфейса идентично тому, что уже отображается на экране; в таком случае он пропускает изменения DOM.

Render
  ↓
Compare
  ↓
No meaningful DOM difference
  ↓
No DOM mutation

Вопрос 3. Принуждает ли перерисовка в React браузер к повторной отрисовке?

Нет. Отрисовка в React и отображение в браузере — это два независимых процесса.

React Render ≠ Browser Paint

Вопрос 4. Что означает процесс согласования?

Согласование — это способ, с помощью которого React сравнивает только что вычисленную структуру интерфейса с предыдущей, чтобы определить, что именно нужно изменить.

Вопрос 5. Что происходит на этапе сохранения изменений?

На этапе сохранения React применяет необходимые изменения в хост-среду — например, обновляет реальные узлы DOM в браузере.

Вопрос 6. В чем разница между useEffect и useLayoutEffect?

Простой способ запомнить это:

useLayoutEffect
→ after DOM commit, before paint

useEffect
→ after commit and generally after paint

Используйте useLayoutEffect, когда вам нужно синхронно измерить или скорректировать макет до того, как пользователь увидит новый кадр. Для большинства побочных эффектов, которые не требуют блокировки процесса отрисовки, используйте useEffect.

В7. Что запускает отрисовку компонента?

Типичные факторы, запускающие отрисовку: первоначальная загрузка компонента, обновление состояния, перерисовка родительского компонента, изменение параметров, обновление контекста или данные из внешнего хранилища.

В8. Как можно оптимизировать процесс отрисовки? [IMP]

Вместо того чтобы по умолчанию использовать useMemo везде, сначала определите место настоящего узкого места. Затем рассмотрите следующие подходы:

1. Храните состояние рядом с местами его использования

Избегайте структуры, при которой состояние находится вверху, а все потомки зависят от него:

App
 ↓
Everything depends on state

prefer:

Feature
 ↓
Local state

Лучше использовать более узкое размещение, когда глобальное состояние на самом деле не требуется.

2. Выборочно мемоизировать дорогостоящие компоненты

const ProductCard = React.memo(ProductCardComponent);

3. Следите за созданием объектов, которое нарушает мемоизацию

Вместо того чтобы постоянно создавать новый объект внутри кода вот так:

<Child config={{ theme: "dark" }} />

Вам может понадобиться стабильный ссылочный элемент, если объект Child обернут в React.memo и важна его идентичность — но не решайте эту проблему, пока она действительно не возникнет.

4. Виртуализация длинных списков

Вместо отрисовки всех 10 000 узлов DOM сразу отрисовывайте только те, которые в данный момент видны.

5. Откладывайте загрузку ресурсов с высокими затратами

const AdminDashboard = lazy(() => import("./AdminDashboard"));

Это сокращает объем JavaScript-кода, который необходимо загрузить и выполнить заранее.

6. Используйте техники отрисовки, подходящие для браузеров

По возможности используйте недорогие свойства, такие как transform и opacity, для анимации, и избегайте принудительных дорогостоящих пересчётов макета на каждом кадре.