HOW TO React

Рендеринг и фиксация

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

Вы узнаете

  • Что означает рендеринг в React
  • Когда и почему React рендерит компонент
  • Этапы отображения компонента на экране
  • Почему рендеринг не всегда приводит к обновлению DOM

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

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

Trigger

1

Render

1

Commit

Шаг 1: Запуск рендеринга

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

  1. Это первоначальный рендеринг компонента.
  2. Состояние компонента (или одного из его предков) было обновлено.

Первоначальный рендеринг

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

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

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

Попробуйте закомментировать вызов root.render() и посмотрите, как компонент исчезнет!

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

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

1

State update...

1

...triggers...

1

...render!

Шаг 2: React рендерит ваши компоненты

После того как вы запускаете рендеринг, React вызывает ваши компоненты, чтобы определить, что отобразить на экране. «Рендеринг» — это вызов ваших компонентов React.

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

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

В следующем примере React вызовет Gallery() и Image() несколько раз:

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

const root = createRoot(document.getElementById('root'))
root.render(<Gallery />);
  • Во время первоначального рендеринга React создаст DOM-узлы для тегов <section>, <h1> и трех тегов <img>.
  • Во время повторного рендеринга React вычислит, какие из их свойств, если таковые имеются, изменились с момента предыдущего рендеринга. Он не будет ничего делать с этой информацией до следующего шага — фазы фиксации.

Рендеринг всегда должен быть чистым вычислением:

  • Одинаковые входные данные — одинаковый результат. При одинаковых входных данных компонент всегда должен возвращать один и тот же JSX. (Когда кто-то заказывает салат с помидорами, он не должен получать салат с луком!)
  • Он занимается только своими делами. Он не должен изменять какие-либо объекты или переменные, существовавшие до рендеринга. (Один заказ не должен влиять на заказы других.)

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

Оптимизация производительности

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

Шаг 3: React фиксирует изменения в DOM

После рендеринга (вызова) ваших компонентов React изменит DOM.

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

React изменяет узлы DOM только в том случае, если между рендерингами есть различия. Например, вот компонент, который перерисовывается каждую секунду с разными пропсами, передаваемыми от его родителя. Обратите внимание, как вы можете добавить текст в <input>, обновив его value, но текст не исчезает при перерисовке компонента:

//Clock.js
export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}
//App.js
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> — или его value!

Эпилог: Рисование браузера

После завершения рендеринга и обновления React DOM браузер перерисует экран. Хотя этот процесс известен как «рендеринг браузера», мы будем называть его «рисованием», чтобы избежать путаницы в документации.

1

Вывод

  • Любое обновление экрана в приложении React происходит в три этапа:
  1. Триггер
  2. Рендеринг
  3. Фиксация
  • Вы можете использовать Strict Mode для поиска ошибок в ваших компонентах
  • React не затрагивает DOM, если результат рендеринга совпадает с предыдущим

On this page