Олег Марков
Как работает рендеринг в React
Введение
Рендеринг в React — это фундаментальное понятие, без которого невозможно представить работу этого популярного фреймворка. Рендеринг отвечает за то, как код на JavaScript превращается в красивые и интерактивные пользовательские интерфейсы в браузере. Механизм работы рендеринга кажется простым на первый взгляд: вы пишете компонент — он отображается. Но на самом деле за этим кроется мощная архитектура: работа с Virtual DOM, диффинг-алгоритмы, оптимизация перерасчёта и обновления, жизненный цикл компонентов. Давайте подробно разберём, как React справляется с рендерингом и что происходит «под капотом».
Что такое рендеринг в React
Рендеринг — это процесс превращения описания UI в реальное DOM-дерево, видимое пользователю. В React вы описываете интерфейс через компоненты на JavaScript или TypeScript, используя синтаксис JSX. Когда компонент рендерится, React создает дерево элементов, которое затем превращается в реальные DOM-элементы.
Есть два типа рендеринга:
- Первичный рендер (Initial Render) — когда компонент впервые появляется на странице.
- Повторный рендер (Re-render) — когда компонент обновляется из-за изменения его состояния (
state
) или свойств (props
).
Важно понимать, что реакция на изменения данных — одна из самых сильных сторон React.
Рендеринг в React - это процесс преобразования React-компонентов в DOM-элементы, которые отображаются в браузере. Понимание того, как React выполняет рендеринг, имеет решающее значение для оптимизации производительности и создания отзывчивых приложений. Если вы хотите детальнее разобраться в механизмах рендеринга в React, узнать о виртуальном DOM и стратегиях оптимизации — приходите на наш большой курс Основы React, React Router и Redux Toolkit. На курсе 177 уроков и 17 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.
Virtual DOM — основа быстрого рендеринга
Как работает Virtual DOM
React не работает напрямую с реальным DOM браузера на каждом шаге. Вместо этого он создает легковесную копию DOM, так называемый Virtual DOM (виртуальный DOM). Посмотрите, как это выглядит:
- Компонент возвращает JSX (например,
<div>Hello</div>
) - React превращает JSX в специальное описание JavaScript-объекта (элемент — “React element”)
- На основе этого объекта строится дерево Virtual DOM
Виртуальный DOM — это нечто вроде снимка реального DOM, который React может быстро анализировать и изменять без манипуляций с браузером.
Почему Virtual DOM эффективен
Обращения к реальному DOM — дорогая операция для браузера (в смысле производительности). Virtual DOM позволяет сравнивать старое и новое состояния интерфейса, изменяя реальные DOM-элементы только там, где это нужно.
Жизненный цикл рендеринга
React-компоненты проходят несколько этапов во время рендеринга. Вот главные из них:
- Монтирование: компонент появляется в DOM впервые.
- Обновление: компонент получает новые
props
или изменяется егоstate
. - Размонтирование: компонент удаляется из DOM.
Каждый этап даёт вам возможность “вмешаться” в процесс с помощью хуков жизненного цикла (например, useEffect
, componentDidMount
и др.).
Когда и почему React инициирует рендеринг
React инициирует рендеринг, когда происходит одно из следующих событий:
- Изменилась функция рендера — компонент впервые вызывается (например, мы добавили его в приложение).
- Обновились свойства — компоненту передали новые значения через
props
. - Обновилось состояние — вызван
setState
в классовом компоненте илиuseState
в функциональном. - Контекст — если компонент подписан на контекст (через
useContext
) и значение контекста поменялось.
Пример: когда срабатывает рендер
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
// Каждый раз, когда вызывается setCount — происходит рендер!
return (
<div>
<p>Текущее значение: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}
// При клике на кнопку: компонент рендерится заново
Как работает процесс повторного рендера
Когда состояние или пропсы компонента изменяются, React начинает новый рендер:
- Запускается функция рендера (компонент возвращает JSX).
- React строит новое дерево Virtual DOM.
- Сравнивает (diffing) новое дерево Virtual DOM со старым.
- Находит отличия (patches).
- Применяет только нужные изменения к реальному DOM.
Пример сравнения деревьев
Допустим, был код:
<div>
<h1>Привет</h1>
<p>Сообщение</p>
</div>
А потом мы изменяем <p>Сообщение</p>
на <p>Новое сообщение</p>
. React увидит различие только в содержимом <p>
и изменит именно этот текст в DOM — остальные элементы трогаться не будут.
React Fiber — сердце современного рендеринга
С версии React 16 процесс рендеринга управляется новым движком — Fiber. Он позволил разбить рендеринг на мелкие части (unit of work), чтобы не блокировать основной поток браузера, делая обновления плавными, а приложения отзывчивыми даже на больших данных.
Кратко о Fiber:
- Позволяет приоритетно обрабатывать задачи (например, анимации первичнее сложных расчётов)
- Даёт гибкость для введения новых возможностей (таких как Concurrent Mode)
- Более “гранулированный” контроль над рендерингом
Методы и хуки, влияющие на рендер компонента
Хук useState
useState
— один из самых популярных хуков для хранения состояния. При вызове функции обновления состояния происходит рендер компонента.
const [value, setValue] = useState(0);
setValue(value + 1); // Компонент ререндерится
Хук useEffect
useEffect
вызывается после рендера. Давайте посмотрим на пример:
useEffect(() => {
// Этот код выполнится после каждого рендера, если не указан второй аргумент
document.title = `Счётчик: ${count}`;
});
Если вы хотите, чтобы эффект сработал только при первом рендере:
useEffect(() => {
// Аналог componentDidMount
}, []);
Хук useMemo и useCallback
Они помогают оптимизировать рендеринг, предотвращая лишние пересчеты функций и значений.
const expensiveValue = useMemo(() => {
// Здесь какая-то тяжелая функция
return complexCalculation(count);
}, [count]); // Пересчитывать только при изменении count
useCallback
— то же, но для функций. Это избавляет дочерние компоненты от лишних рендеров при неизменности функций в пропсах.
Оптимизация рендеринга
Мемоизация компонентов — React.memo
Если ваш компонент зависит только от props и не должен рендериться при любом изменении родителя, используйте React.memo
:
const MyComponent = React.memo(function MyComponent({ value }) {
// Ререндер только если value изменился
return <div>{value}</div>;
});
shouldComponentUpdate и PureComponent
Для классовых компонентов оптимизация ререндера достигается двумя инструментами:
shouldComponentUpdate(nextProps, nextState)
— позволяет отменить рендер, если входные данные не изменились.PureComponent
— это компонент, который реализует поверхностное сравнение пропсов и состояния.
class MyComp extends React.PureComponent {
render() {
// Будет рендериться только если изменились props или state
return <span>{this.props.value}</span>;
}
}
Ключи при рендеринге списков
При рендеринге массивов элементов через .map()
обязательно указывайте уникальный ключ каждому элементу. Это позволяет React корректно отслеживать изменения, вставки и удаления элементов.
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
Важно: Ключ должен быть уникальным и неизменным для элемента списка.
Контроль над рендерингом: форсированный и предотвращённый рендер
Форсированный рендер (forceUpdate)
В редких случаях можно принудительно инициировать рендер с помощью forceUpdate()
(классовые компоненты). В функциональных компонентах этого нет, но обычно это и не требуется.
Пропуск рендера
Для предотвращения рендера используйте оптимизации (React.memo
, shouldComponentUpdate
) или не меняйте состояние без необходимости.
Как React рендерит дочерние компоненты
Когда родительский компонент обновляется, все его дочерние компоненты, по умолчанию, тоже проходят рендер. Чтобы рендерились только затронутые данные — используйте вышеописанные оптимизации.
function Parent({ value }) {
return (
<Child value={value} />
);
}
// Child рендерится при каждом рендере Parent, если не используется React.memo
Практический пример: оптимизация рендеринга
Представьте, что у вас есть список задач и форма добавления новой задачи. Посмотрите, как оптимизировать рендеринг списка при добавлении задачи.
// Большой компонент списка задач
const TaskList = React.memo(({ tasks }) => {
return tasks.map(task => (
<TaskItem key={task.id} task={task} />
));
});
// Компонент добавления задачи
function AddTaskForm({ onAdd }) {
const [value, setValue] = useState('');
return (
<form onSubmit={e => {
e.preventDefault();
onAdd(value);
setValue('');
}}>
<input value={value} onChange={e => setValue(e.target.value)} />
<button type="submit">Добавить</button>
</form>
);
}
function TodoApp() {
const [tasks, setTasks] = useState([]);
// Добавляем задачу не мутируя массив — это важно!
const addTask = task => setTasks(current => [...current, { id: Date.now(), name: task }]);
return (
<>
<AddTaskForm onAdd={addTask} />
<TaskList tasks={tasks} />
</>
);
}
Что здесь происходит:
- Пока не добавляются новые задачи, рендерится только форма;
TaskList
обернут вReact.memo
, и если массивtasks
не изменился, он не рендерится заново;- При добавлении
addTask
создает новый массив, чтобы сработала оптимизация по ссылке.
Разница между клиентским и серверным рендерингом
В React вы можете использовать и клиентский, и серверный рендеринг:
- Клиентский рендеринг (CSR): Рендер происходит в браузере пользователя. Это классический подход: начальная загрузка быстрая, но индексация поисковиками хуже.
- Серверный рендеринг (SSR): HTML формируется на сервере (например, с помощью Next.js или Remix) и отправляется браузеру уже в собранном виде. Преимущества — SEO, быстрая “первая отрисовка”.
Также есть гибридные подходы: статическая генерация, инкрементальные обновления страниц и т.п.
Заключение
Рендеринг в React — не просто банальное отображение данных. Это целая система, направленная на высокую производительность, отзывчивость и масштабируемость современных приложений. Вам важно понимать, как работают Virtual DOM, алгоритмы сравнения, что инициирует рендер и как оптимизировать его с помощью хуков и инструментов React. Это не только экономит ресурсы, но и позволяет создавать более быстрые и стабильные приложения.
Понимание рендеринга - важный аспект разработки. Для создания сложных приложений необходимо уметь управлять состоянием и организовывать роутинг. Рассмотрите курс Основы React, React Router и Redux Toolkit, чтобы узнать больше. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в основы React уже сегодня.
Частозадаваемые технические вопросы и ответы
Как предотвратить лишние рендеры при передаче функций в props?
Если функция создаётся при каждом рендере компонента-родителя, дочерний компонент будет ререндериться даже если props не изменились. Оберните функцию в useCallback
, указывая зависимости по необходимости.jsx
const onClick = useCallback(() => {
// Обработчик клика
}, []);
Это позволит сохранить ссылочную целостность функции между рендерами.
Почему key нельзя использовать по индексу в списках?
Если использовать индекс массива как ключ, при изменении порядка элементов или их удалении React будет некорректно сопоставлять элементы, что приведёт к багам в интерфейсе (например, неправильное сохранение состояния дочерних компонентов). Используйте уникальные и постоянные id для ключей, если это возможно.
Как "видит" React разницу между setState и прямым изменением state?
Если вы напрямую меняете state объекта, React не "узнает", что его нужно ререндерить. Изменяйте только с помощью функций обновления (setState
, setValue
в useState).
Плохой пример:
jsx
state.value = 42; // Рендер не произойдёт!
Правильный способ:
jsx
setState({ value: 42 }); // Инициируется ререндер
Можно ли рендерить компонент только при изменении определенного props?
Да, для этого воспользуйтесь React.memo
c функцией сравнения.jsx
const MyComp = React.memo(
function MyComp({ importantProp }) {
return <>{importantProp}</>;
},
(prev, next) => prev.importantProp === next.importantProp
);
Так компонент будет обновляться только при изменении указанного свойства.
Как отследить причину рендера компонента?
Установите инструмент why-did-you-render — он покажет в консоли браузера причину ререндера компонентов в вашем приложении. Это очень удобно для отладки и оптимизации.
Постройте личный план изучения React до уровня Middle — бесплатно!
React — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по React
Лучшие курсы по теме

React и Redux Toolkit
Антон Ларичев
TypeScript с нуля
Антон Ларичев