Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Reducers, Context и внешние хранилища
reducers_context

Reducers, Context и внешние хранилища

useReducer, разделение состояния и команд, Context, useSyncExternalStore и границы глобального состояния.

Открыть лабораториюv0.1.0Запускается локально из публичного репозитория

Reducers, Context и внешние хранилища

Глобальное состояние не является целью: задача — сделать переходы явными и ограничить область подписки.

#Результат урока

Вы выберете reducer для сложных переходов, безопасно распространите зависимости через Context и подключите внешний store без tearing.

До начала достаточно state ownership, immutable updates и discriminated unions. В конце должен появиться не конспект, а типизированная корзина с reducer, разделёнными contexts и внешним online-status store.

#1. Reducer как чистая таблица переходов

Reducer получает state и action и возвращает следующий state. Он не отправляет запросы, не читает время и не мутирует вход. Чистота позволяет отдельно проверить переходы и повторить action. Event type — discriminated union с минимальным payload.

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

type Action = | { type: 'added'; item: Item } | { type: 'removed'; id: string }; function reducer(state: State, action: Action): State { switch (action.type) { case 'added': return { ...state, items: [...state.items, action.item] }; case 'removed': return { ...state, items: state.items.filter(x => x.id !== action.id) }; } }

Action описывает событие, а reducer — единственное место изменения этой модели.

Рабочая проверка. Проверьте каждый переход без render компонента.

Типичная поломка. Вызывать fetch внутри reducer при action submitted.

#2. Action описывает событие, не setter

Action names вроде itemAdded объясняют произошедшее; setState переносит детали модели к каждому caller. Payload содержит необходимые данные, но не DOM Event. События дают журнал для диагностики и облегчают смену внутренней структуры state.

Рабочая проверка. Замените generic setField доменными actions checkout.

Типичная поломка. Передавать reducer MouseEvent и читать target внутри перехода.

#3. Инициализация и reset reducer

Третий аргумент useReducer задаёт lazy initializer. Props не переинициализируют reducer автоматически. Reset может быть action с исходными данными или новая component identity. Решение зависит от того, является ли новая сущность новым экземпляром или событием текущей workflow.

Рабочая проверка. Реализуйте reset checkout после подтверждённого заказа, не читая изменяемый initial object.

Типичная поломка. Мутировать объект initialState и использовать его для каждого reset.

#4. Context как доставка зависимости

Context избавляет от передачи prop через промежуточные уровни, но создаёт неявную зависимость consumer от provider. Value должен быть узким, provider — размещён близко к владельцу. Context default не является fallback при provider с undefined; custom hook обычно бросает понятную ошибку без provider.

Рабочая проверка. Создайте useCheckout с явной ошибкой при использовании вне CheckoutProvider.

Типичная поломка. Сложить auth, theme, router, API и весь state приложения в один context object.

#5. Разделение state и dispatch contexts

Dispatch useReducer имеет стабильную identity. Если state и commands лежат в одном новом object value, каждый state update меняет value для всех consumers. Раздельные contexts позволяют command-only consumer не подписываться на state. Для частых больших обновлений могут понадобиться selectors или внешний store.

Рабочая проверка. Профилируйте кнопку, использующую только dispatch, до и после разделения contexts.

Типичная поломка. Мемоизировать огромный context object, не измерив consumers и частоту изменений.

#6. Selectors и производные данные

Selector — чистая функция State -> Value. Он централизует знание структуры и упрощает тесты. Мемоизация нужна только для дорогого вычисления или стабильной ссылки у подписки; selector не должен отправлять запросы. Внешние store-библиотеки могут подписывать по selector, обычный Context — нет автоматически.

Рабочая проверка. Выделите selectors subtotal/discount/total и проверьте граничные случаи.

Типичная поломка. Хранить subtotal и total отдельно от items и обновлять в каждом reducer case.

#7. useSyncExternalStore и snapshot contract

Внешний store подключается через subscribe и getSnapshot; для SSR добавляется getServerSnapshot. Snapshot должен быть кэшированным/immutable: пока store не изменился, повторный вызов возвращает значение, равное по Object.is. React координирует чтения, чтобы избежать tearing между concurrent renders.

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

const online = useSyncExternalStore( networkStore.subscribe, networkStore.getSnapshot, networkStore.getServerSnapshot, );

Не создавайте новый object внутри getSnapshot без кэша: React сочтёт store изменившимся при каждом чтении.

Рабочая проверка. Сделайте online store и проверьте unsubscribe после unmount.

Типичная поломка. getSnapshot: () => ({ online: navigator.onLine }) с новым object всегда.

#8. Выбор локального, Context или внешнего store

Начинайте с локального state у владельца. Context нужен для относительно стабильной зависимости по subtree. Внешний store оправдан множеством независимых подписчиков, частыми обновлениями, selectors, данными вне React или межстраничным lifetime. Server cache не надо автоматически копировать в client store.

Рабочая проверка. Классифицируйте state приложения: field draft, theme, auth session, server list, websocket ticker.

Типичная поломка. Помещать каждый input keystroke в глобальный store.

#Лаборатория

Соберите checkout state machine. Reducer обрабатывает add/remove/setQuantity/coupon/submit outcomes, Context разделяет state и commands, а внешний store сообщает online status через useSyncExternalStore. Добавьте selector tests.

Критерий завершения: reducer чист и исчерпывающ, consumers не перерисовываются от неиспользуемого command context, external snapshot стабилен. Сначала зафиксируйте наблюдаемое поведение тестом или измерением, затем меняйте реализацию.

#Источники для сверки

  • React useReducer
  • React useContext
  • React useSyncExternalStore
  • React: Scaling Up with Reducer and Context

Устранение неисправностей

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Effects, refs и внешние системы