Перейти к основному содержимому

React

Для чего модуль

Перейти от уровня «знаю хуки» к уровню «могу проектировать и защищать React-решение под production-нагрузку».

Результат после прохождения

  1. Вы понимаете, почему компонент ререндерился, и доказываете это через инструменты.
  2. Вы проектируете state-границы так, чтобы UI был предсказуемым и поддерживаемым.
  3. Вы избегаете типичных async/effect ошибок и stale-data багов.
  4. Вы связываете оптимизации с метриками UX, а не с «красивым кодом».

Термины и аббревиатуры

ТерминКоротко
ReconciliationСравнение virtual tree
HookAPI React для логики
Stale closureУстаревшие значения в замыкании
MemoizationКэш вычислений/рендера
HydrationАктивация клиентского UI

Фокус по грейдам

  1. Junior: понимать базовые механики и объяснять их простыми примерами.
  2. Middle: применять тему в продуктовых сценариях с учетом рисков и ограничений.
  3. Senior: управлять архитектурными trade-offs, метриками и эволюцией решения.

Как работать с модулем

  1. Каждый урок проходите через mini-case из реального экрана.
  2. Для каждого решения фиксируйте: симптом -> гипотеза -> замер -> изменение -> результат.
  3. Любую оптимизацию проверяйте profiler/метриками.

Программа модуля

Урок 1. Рендеринг и reconciliation

Цель: понимать рендер React как конкретную механику, а не «магический процесс».

Что происходит при обновлении

  1. React строит новое дерево элементов.
  2. Сравнивает с предыдущим (reconciliation).
  3. Применяет изменения в commit phase.

Ключевой вывод: оптимизировать нужно не «количество вызовов функций», а дорогие пересоздания/commit-операции.

key и стабильность идентичности

Нестабильный key (например, index в reorder-списке) приводит к remount и потере локального state.

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

Profiler: что смотреть

  1. Какие компоненты часто ререндерятся.
  2. Длительность render/commit.
  3. Почему ререндер произошел (props, state, parent render).

Где ломается в проде

  1. Использование index как key в изменяемых списках.
  2. Случайные remount из-за нестабильных ключей/условного дерева.
  3. «Оптимизация» без замера, которая усложняет код и не улучшает UX.

Мини-задача (обязательная)

Возьмите список с drag/reorder и сравните два варианта: key=index и key=item.id. Снимите profiler и объясните разницу в поведении и стоимости обновлений.

Что спросит интервьюер: почему компонент ререндерился и как вы это доказали.

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

Урок 2. Hooks и side effects

Цель: писать эффекты без гонок, утечек и скрытых зависимостей.

useEffect как синхронизация с внешним миром

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

Зависимости и stale closures

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

Асинхронные эффекты: cancel first

useEffect(() => {
const ac = new AbortController();

(async () => {
try {
const res = await fetch(`/api/search?q=${query}`, { signal: ac.signal });
const data = await res.json();
setItems(data.items);
} catch (e) {
if (e.name !== 'AbortError') setError(e);
}
})();

return () => ac.abort();
}, [query]);

Где ломается в проде

  1. Эффект с [], который использует динамические значения.
  2. Два параллельных запроса обновляют один state в неопределенном порядке.
  3. Cleanup отсутствует для подписки/таймера.

Мини-задача (обязательная)

Сделайте экран поиска с debounce + cancel предыдущего запроса. Зафиксируйте, как предотвращается stale UI.

Что спросит интервьюер: когда массив зависимостей [] корректен, а когда это баг.

Критерий готовности по уроку: вы можете написать effect без race-condition и объяснить, почему он безопасен.

Урок 3. Управление состоянием

Цель: отделять local state, shared state и server state, чтобы не перегружать архитектуру.

State ownership

Главный вопрос: где должен жить источник истины.

  1. Локальный state - ближе к компоненту.
  2. Shared UI state - поднимаем на ближайший общий уровень.
  3. Server state - отдельные механизмы загрузки/кэширования/инвалидации.

Антипаттерн «глобальный store на все»

Проблемы:

  1. Сильная связанность модулей.
  2. Сложная отладка побочных эффектов.
  3. Ненужные ререндеры широких частей дерева.

Практика декомпозиции

Для сложного экрана выделяйте:

  1. State владения данными.
  2. State UI-представления.
  3. Derived data (мемоизируемое вычисление).

Где ломается в проде

  1. Дублирование одного и того же state в нескольких местах.
  2. Мутация объекта в store и «невидимые» изменения.
  3. Смешение server state и локального UI state.

Мини-задача (обязательная)

Возьмите экран фильтруемого каталога и разложите state по ownership: что локально, что shared, что server state, что derived.

Что спросит интервьюер: когда Context достаточно, а когда нужен выделенный state-слой.

Критерий готовности по уроку: вы можете аргументированно описать state-архитектуру экрана и показать, где она защищает от регрессий.

Урок 4. Надежность и перформанс

Цель: проектировать UI, устойчивый к ошибкам, задержкам и частичным отказам.

Error boundaries и fail-safe UI

  1. Ошибка одного виджета не должна «ронять» весь экран.
  2. Пользователь должен получать внятный fallback, а не белый экран.

Loading/empty/error как часть контракта

Для каждого запроса заранее проектируйте 4 состояния: loading, success, empty, error. Это снижает количество «невидимых» багов на edge-cases.

Performance с привязкой к UX

  1. Сначала измеряем baseline.
  2. Оптимизируем конкретный bottleneck.
  3. Проверяем влияние на сценарий пользователя.

Где ломается в проде

  1. «Оптимизация» через memo/useMemo без эффекта на UX.
  2. Отсутствие fallback при частичном падении API.
  3. Нет связи frontend ошибки с backend trace.

Мини-задача (обязательная)

Соберите perf-case: проблема рендера -> baseline -> изменение -> итоговый замер. Отдельно опишите error/fallback стратегию для этого экрана.

Что спросит интервьюер: почему ваша оптимизация реально улучшает UX, а не только «красиво выглядит в коде».

Критерий готовности по уроку: вы можете защитить React-решение через надежность и измеримый эффект на пользовательский сценарий.

Практика

  1. Разберите 12 вопросов: 8 из Middle JavaScript и React, 4 из Senior Frontend.
  2. Сделайте profiler-разбор одного реального экрана с фиксацией до/после.
  3. Реализуйте экран поиска с debounce + cancel + empty/error states.
  4. Подготовьте state ownership-карту сложного экрана.
  5. Зафиксируйте один reliability-case: partial API failure и поведение UI.
  6. Соберите сценарий loading/error/empty в Песочнице и проверьте устойчивость UI.

Связь с треками и вопросами

  1. Треки: Junior трек, Middle трек, Senior трек.
  2. Вопросы: Middle JavaScript и React, Senior Frontend.
  3. Повторение: 3-5 вопросов без подсказок -> сверка с модулем -> повтор через 24 часа.

Критерий готовности

Вы можете объяснить React-решение как инженерный кейс: контекст -> архитектурный выбор -> риски -> замеры -> результат.

Артефакты после модуля

  1. Profiler-отчет до/после по одному экрану.
  2. State ownership-документ для сложного UI.
  3. Набор из 8 сильных interview-ответов по React с примерами.
  4. Один reliability runbook для UI-инцидента.

Куда дальше

  1. Next.js
  2. TypeScript
  3. React