React¶
React — это библиотека для построения пользовательских интерфейсов. Главная идея React:
- разработчик описывает, что должно быть на экране для текущего состояния данных;
- React сам решает, как обновить интерфейс с минимальным количеством изменений.
React не равен браузеру и не равен DOM. Нужно разделять:
react— ядро, которое умеет описывать UI, считать дерево компонентов и управлять обновлениями;react-dom— рендерер для браузера;react-native— рендерер для мобильных платформ;- другие рендереры — для CLI, canvas, WebGL и других сред.
React не является полным фреймворком. Сам по себе он решает в первую очередь слой UI:
- компоненты;
- состояние;
- обновление интерфейса;
- реактивный рендер.
А вот маршрутизация, работа с сервером, глобальное состояние, сборка и архитектура приложения — это уже отдельные решения вокруг React.
Базовая модель React¶
Интерфейс собирается из компонентов. Компонент — это функция, которая: - получает входные данные - вычисляет, что нужно показать - возвращает JSX
Компоненты можно вкладывать друг в друга и собирать из них все приложение.¶
Props — это входные данные компонента от родителя. Их свойства: - приходят сверху вниз - считаются внешними по отношению к компоненту - сам компонент не должен их мутировать
function Button({ title, onClick, children, ...props }) {
return (
<button onClick={onClick} {...props}>
{title}
{children}
</button>
);
}
children— специальный prop для вложенного содержимого;...props— способ пробросить остальные свойства дальше, например в DOM-элемент.
State — это внутреннее состояние компонента. Если props приходят извне, то state живет внутри компонента и меняется через React API. Изменение state не меняет DOM напрямую. Вместо этого React запускает новый цикл рендера.
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
¶
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
В React данные обычно идут сверху вниз:
- родитель передает props ребенку;
- ребенок может уведомить родителя через callback;
- родитель меняет state;
- React заново пересчитывает UI.
Схема:
state родителя
-> props ребенку
-> действие пользователя
-> callback вверх
-> обновление state
-> новый рендер
Это делает поведение интерфейса более предсказуемым.
JSX и React-элементы¶
JSX — это синтаксический сахар над вызовами создания React-элементов.
То есть:
это не HTML и не шаблонный язык. После трансформации это становится вызовом функции из React runtime.
В современном виде примерно так:
или
3.2. Что такое React-элемент¶
React-элемент — это обычный JavaScript-объект, который описывает будущий UI.
Упрощенно:
Важно:
- React-элемент не является DOM-узлом;
- это только описание того, что React хочет видеть;
- элементы создаются заново при каждом рендере.
key¶
key нужен React, чтобы правильно сопоставлять элементы в списках и при перестановках.
Плохо:
Хорошо:
Стабильный key помогает React понять:
- что нужно обновить;
- что нужно сохранить;
- что нужно удалить;
- что просто переместилось.
Ядро React и рендереры¶
React специально отделяет: - логику вычисления UI; - применение этих вычислений к конкретной платформе.
React core - знает, что нужно показать
Renderer - знает, как это показать в конкретной среде
| Рендерер | Где используется | Что делает |
|---|---|---|
react-dom |
браузер | работает с DOM, hydration, SSR API |
react-native |
iOS / Android | работает с нативными view-компонентами |
| custom renderer | CLI / canvas / WebGL / нестандартные среды | применяет React-модель к своей платформе |
Компоненты React сами по себе не обязаны знать о DOM.
Но если внутри компонента ты используешь:
windowdocument- DOM API
то ты уже завязываешь код на web-среду.
Поэтому полезно различать:
- платформенно-независимую логику;
- код, завязанный на браузер.
React и DOM¶
DOM — это объектное представление HTML-документа в браузере. Браузер строит DOM-дерево из HTML.
Есть три разных сущности: 1. React-элементы 2. Fiber-дерево 3. Реальный DOM
Виртуальный DOM полезен как упрощенная метафора, но технически в React лучше различать два уровня:
- React-элементы — декларативный план интерфейса
- Fiber-дерево — рабочая структура React, в которой хранится состояние вычисления
Поэтому фраза "React сравнивает virtual DOM" полезна для общего понимания, но внутри React все устроено точнее.
Fiber — это внутренняя структура React, по одному рабочему узлу на каждый элемент дерева интерфейса.
Fiber хранит:
- тип компонента;
- key;
- текущие props;
- предыдущие props;
- текущее состояние;
- связи с родителем, ребенком и соседями;
- пометки о том, что нужно сделать на commit.
React поддерживает две версии дерева:
current— то, что уже показано пользователю;workInProgress— новое дерево, которое React сейчас рассчитывает.
Во время рендера React не ломает уже показанный экран. Он строит новую рабочую версию дерева, а потом в commit атомарно применяет результат.
Fiber дал React возможность:
- считать обновления по частям;
- приостанавливать вычисления;
- продолжать позже;
- расставлять приоритеты;
- не блокировать интерфейс тяжелой работой.
То есть Fiber — это не "копия DOM", а внутренняя структура управления работой React.
Reconciler — это часть React, которая сравнивает: - старое описание UI; - новое описание UI.
И решает:
- что обновить;
- что оставить;
- что удалить;
- что смонтировать заново.
React смотрит прежде всего на:
- тип элемента;
key.
Если тип и key совпадают, React старается обновить существующий узел.
Если тип или key поменялись, React считает, что это уже другой элемент:
- старый узел размонтируется;
- новый будет смонтирован заново.
При неправильных ключах можно получить:
- потерю фокуса;
- лишние размонтирования;
- сброс локального state;
- лишние DOM-операции.
Render phase и commit phase¶
React обновляет UI в две разные фазы
Render phase¶
Это чистая вычислительная фаза. В render phase React:
- вызывает функцию компонента;
- читает props, state, context;
- выполняет useMemo и useCallback;
- строит новое дерево элементов;
- обновляет Fiber-дерево;
- помечает, что надо изменить.
В этой фазе нельзя делать побочные действия: - нельзя мутировать DOM; - нельзя запускать ручные подписки; - нельзя рассчитывать на то, что render вызовется только один раз.
Важно: - render может быть повторен; - render может быть прерван; - render может быть отброшен без commit.
Commit phase¶
Это фаза применения изменений. В commit phase React:
- создает, удаляет и перемещает реальные DOM-узлы;
- обновляет атрибуты и обработчики;
- обновляет ref;
- запускает useLayoutEffect;
- после кадра запускает useEffect.
Commit короче и критичнее по времени, потому что именно после него пользователь видит результат.
state/props changed
> render phase
> вычислить новое дерево
> найти различия
> commit phase
> применить изменения в DOM
> layout effects
> paint
> passive effects
Очень важно не путать три разных процесса:
- render в React
- commit в React
- paint в браузере
React render - Это пересчет дерева компонентов и JSX. Пользователь этого еще не видит.
React commit - Это момент, когда React реально меняет DOM.
Browser paint - Это уже работа браузера: - style calculation; - layout; - paint; - compositing.
Упрощенно:
Поэтому фраза "React перерисовал компонент" не означает, что браузер обязательно перерисовал весь экран.
Часто это означает только:
- React пересчитал компонент;
- сравнил старое и новое описание;
- внес небольшой патч;
- браузер обновил только нужное.
Жизненный Цикл Компонента¶
Монтирование — это первое появление компонента в дереве. Что происходит:
1. React вызывает компонент, получает JSX
2. React создает DOM-узлы
3. React назначает ref
4. React запускает useLayoutEffect
5. браузер рисует кадр
6. React запускает useEffect
Обновление происходит, если:
- изменились props;
- изменился state;
- изменился context;
- изменился key;
- родитель заново отрендерил дерево;
- внешняя подписка вызвала обновление.
Что происходит: 1. новый render 2. сравнение со старым деревом 3. commit изменений 4. cleanup старых эффектов 5. запуск новых эффектов
Размонтирование — это удаление компонента из дерева. Что происходит:
- удаляются DOM-узлы;
- выполняются cleanup-функции эффектов;
- ref сбрасываются.
Что не является размонтированием: - скрытие через CSS; - пустой контент внутри компонента, если сам компонент остался в дереве.
Что важно помнить
Если в эффекте создаются: - таймер; - подписка; - наблюдатель; - соединение;
то их нужно очищать в cleanup.
Context¶
Context нужен, чтобы не прокидывать одни и те же props через много промежуточных уровней. Типичные данные для context: - тема; - язык; - текущий пользователь; - настройки приложения.
Пример:
const ThemeContext = createContext("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Page />
</ThemeContext.Provider>
);
}
function Button() {
const theme = useContext(ThemeContext);
return <button data-theme={theme}>Save</button>;
}
Что важно:
- при изменении
valueперерисуются потребители этого context; - если засунуть в один context слишком много разнородных данных, можно получить лишние рендеры;
- полезно дробить context по зонам ответственности.
25. React Hooks¶
Hooks — это функции, через которые функциональные компоненты получают:
- состояние;
- эффекты;
- context;
- refs;
- мемоизацию;
- доступ к другим возможностям React.
Главное правило хуков:
- вызывать только на верхнем уровне компонента;
- не вызывать внутри циклов, условий и вложенных функций;
- или вызывать внутри своих custom hooks.
25.1. useState¶
Локальное состояние компонента.
25.2. useEffect¶
Побочные эффекты после commit.
25.3. useLayoutEffect¶
Синхронная работа после DOM-мутаций, но до показа кадра.
25.4. useRef¶
Доступ к DOM и хранение мутабельного значения без ререндера.
25.5. useMemo¶
Мемоизация дорогого вычисления.
25.6. useCallback¶
Мемоизация функции.
25.7. useContext¶
Чтение значения context.
25.8. Когда не надо мемоизировать все подряд¶
useMemo и useCallback полезны не сами по себе, а когда:
- действительно есть дорогая операция;
- или важна стабильность ссылки для оптимизации дочерних компонентов.
Иначе это может только усложнять код.
26. Производительность и диагностика¶
Оптимизация в React — это не "сделать меньше JSX", а уменьшить лишнюю работу там, где она реально есть.
26.1. Что помогает¶
- правильные
key; - нормальная структура state;
- разделение компонентов;
React.memo;useMemo;useCallback;startTransition;useDeferredValue;- виртуализация длинных списков;
- профилирование.
26.2. React.memo¶
Позволяет пропустить рендер дочернего компонента, если его props не изменились.
26.3. Profiler и DevTools¶
Для диагностики лучше не гадать, а смотреть:
- кто рендерится;
- почему рендерится;
- сколько это стоит по времени.
27. Роутинг в React-приложении¶
В исходном документе эта тема упоминалась как заметка на будущее, поэтому здесь коротко фиксирую место темы в общей картине.
Для роутинга в React обычно используют react-router-dom.
Он решает:
- соответствие URL и экранов;
- навигацию без полной перезагрузки;
- параметры маршрута;
- вложенные маршруты;
- редиректы;
- защиту маршрутов.
Типичные сущности:
- маршруты;
Link;Navigate;useParams;useNavigate.
В старых материалах можно встретить useHistory, но в современном React Router чаще используют useNavigate.
28. Архитектура React под капотом в одной схеме¶
Компоненты
-> JSX
-> React-элементы
-> Fiber-дерево
-> reconciler сравнивает старое и новое дерево
-> render phase вычисляет изменения
-> commit phase применяет изменения
-> react-dom обновляет DOM
-> браузер делает paint
Или еще короче:
данные меняются
-> React пересчитывает UI
-> React находит минимальный патч
-> DOM обновляется точечно
-> пользователь видит новый экран
29. Короткая ментальная модель¶
Правильнее всего думать о React так:
- интерфейс — это функция от состояния;
- JSX — это описание желаемого UI;
- React не рисует браузер сам, а управляет обновлением host-среды через рендерер;
- render — это вычисление;
- commit — это применение;
- DOM обновляется не весь, а только там, где реально есть изменения.
30. Темы, которые еще стоит дописать в будущем¶
Ниже темы, которые логично добавить в этот конспект следующим этапом.
30.1. По React core¶
- StrictMode
- Portals
- Fragments
memo,forwardRef,lazyuseReduceruseIduseDeferredValueuseTransitionuseSyncExternalStoreuseImperativeHandle
30.2. По React DOM и браузеру¶
- детально про hydration mismatch
- event replay при гидратации
- ресурсные подсказки
preload,preconnect,preinit - отличие layout, paint, reflow, repaint
30.3. По архитектуре приложений¶
- container/presentational components
- feature-based folder structure
- разделение UI state и server state
- кастомные хуки как слой композиции логики
30.4. По данным и серверу¶
- fetch patterns подробнее
- race conditions и отмена запросов
- React Query / TanStack Query
- SWR
- optimistic updates
30.5. По роутингу¶
react-router-domподробно- nested routes
- layout routes
- private routes
- public routes
- guards
- loaders/actions, если использовать data routers
30.6. По формам¶
- валидация форм
- React Hook Form
- Formik
- сложные формы с вложенными полями
30.7. По производительности¶
- виртуализация списков
- splitting bundles
- lazy loading
- profiling workflows
- анти-паттерны оптимизации
30.8. По экосистеме¶
- Node.js как среда разработки
- Vite
- Webpack
- Babel
- TypeScript + React
- HMR
- dev server
30.9. По серверным возможностям¶
- SSR на практике
- streaming SSR на практике
- React Server Components подробнее
- Next.js как React-фреймворк поверх этих возможностей
30.10. По тестированию¶
- React Testing Library
- unit tests компонентов
- integration tests UI
- тестирование хуков
- тестирование роутинга и асинхронного UI