Проблема подписок на внешнее хранилище
Узнайте, что такое tearing и почему подписки через useState или useRef на внешние хранилища небезопасны в React с конкурентным выполнением.
«Проблема подписок на внешнее хранилище» — бесплатный урок React Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.
Что такое внешнее хранилище
Внешнее хранилище — это любой источник состояния, расположенный за пределами дерева React, например хранилище Redux, браузерный API вроде matchMedia или обычный объект на уровне модуля со своим механизмом подписки.
React не владеет этим состоянием, поэтому ему необходимо подписываться на изменения и считывать снимки, чтобы синхронизировать компоненты с текущим содержимым хранилища.
Наивная подписка через useState и useEffect
Распространённый подход считывает значение хранилища в useState, а затем использует useEffect для подписки и вызова setState при каждом изменении хранилища. На первый взгляд кажется, что так компонент всегда получает актуальные данные.
Для простого синхронного отображения это работает, но здесь есть скрытый недостаток, который проявляется, когда React отображает компоненты одновременно и может приостановить или перезапустить работу в процессе.
Проблема расслоения данных
Расслоение происходит, когда разные части пользовательского интерфейса показывают разные значения одного и того же хранилища во время одного прохода отображения. Параллельное отображение может прерывать и возобновлять работу, а хранилище за это время может измениться.
В результате экран становится несогласованным: один компонент показывает старое значение, а другой — новое. До появления возможностей параллельного отображения такая несогласованность фактически была невозможна.
Пример: расслоение при смене тёмной темы во время отображения
Представьте, что множество компонентов считывает хранилище темы, пока React выполняет параллельное отображение. Если пользователь переключит тёмный режим в середине этого процесса, компоненты, отображённые до переключения, покажут светлую тему, а отображённые после него — тёмную.
На один кадр экран станет наполовину светлым и наполовину тёмным, потому что значение хранилища изменилось во время отображения, которое React не обработал как атомарное.
Почему useEffect срабатывает слишком поздно
Эффекты выполняются после фиксации результата отображения, поэтому подписка на основе useEffect не может гарантировать, что значение, прочитанное во время отображения, совпадает с содержимым хранилища в момент фиксации. Подписка срабатывает слишком поздно, чтобы предотвратить несогласованное отображение.
К тому времени, когда эффект повторно подпишется и обновит состояние, несогласованный кадр уже мог быть показан пользователю.
Устаревший useMutableSource
Более ранний экспериментальный API под названием useMutableSource пытался решить задачу безопасного чтения внешних данных при параллельном отображении, но его оказалось трудно правильно использовать, и он так и не был выпущен в стабильном виде.
Команда React заменила его более простым и удобным примитивом, который обеспечивает те же гарантии согласованности без таких подводных камней.
useSyncExternalStore как решение
useSyncExternalStore — официальный хук для безопасной при параллельном отображении подписки на внешние хранилища. Он координирует работу с React, благодаря чему чтения остаются согласованными в пределах одного отображения и расслоение компонентов не возникает.
Он стал рекомендуемым примитивом для авторов библиотек, которым необходимо надёжно подключать внешнее состояние к React при параллельном отображении.
Какие библиотеки это затронуло
Напрямую это затронуло библиотеки состояния, подписывающиеся на внешние хранилища, включая Redux, MobX и Valtio. Каждой из них потребовалось перейти на новый примитив, чтобы сохранить безопасность после появления возможностей параллельного отображения.
Поэтому их интеграции с React были обновлены: теперь внутри они вызывают useSyncExternalStore, а не полагаются на прежний шаблон с useState и useEffect.
Согласованный снимок и безопасность при конкурентном рендеринге
Хук гарантирует, что каждый компонент, читающий хранилище во время рендеринга, видит один и тот же снимок, и принудительно выполняет синхронный повторный рендеринг, если хранилище изменяется так, что иначе возник бы разрыв.
Именно сочетание согласованного снимка и безопасности при конкурентном рендеринге является главным преимуществом этого хука по сравнению с подписками, реализованными вручную.
Примитив, рассчитанный на будущее
Используя useSyncExternalStore, Вы сохраняете корректность подписок по мере развития модели рендеринга React. Вы полагаетесь на гарантии фреймворка, вместо того чтобы несовершенно реализовывать их самостоятельно.
Это делает его надежным рекомендуемым способом подключить любой внешний источник состояния к компонентам React.
Быстрая проверка: проблема разрывов
Убедитесь, что понимаете, что означает разрыв при конкурентном рендеринге.
Итоги: проблемы подписки на внешнее хранилище
Внешние хранилища находятся за пределами React, а наивная подписка с помощью useState и useEffect не может предотвратить разрывы при конкурентном рендеринге. Эффекты выполняются слишком поздно, чтобы сохранить согласованность чтения в пределах одного рендеринга.
useSyncExternalStore заменяет устаревший useMutableSource и обеспечивает согласованный снимок с безопасностью при конкурентном рендеринге. Такие библиотеки, как Redux, MobX и Valtio, приняли его как надежный рассчитанный на будущее примитив для подписок на хранилища.
Часто задаваемые вопросы
Урок «Проблема подписок на внешнее хранилище» бесплатный?
Да — полный текст урока «Проблема подписок на внешнее хранилище» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Проблема подписок на внешнее хранилище»?
Узнайте, что такое tearing и почему подписки через useState или useRef на внешние хранилища небезопасны в React с конкурентным выполнением. Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Проблема подписок на внешнее хранилище»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Проблема подписок на внешнее хранилище
- API и параметры useSyncExternalStore
- Подписка на API браузера
- Создание пользовательского хранилища с useSyncExternalStore