Почему возникает ошибка при вызове методов subscribe и getState при монтировании компонента?
Что проверяют
Понимаете ли вы, что хранилище это внешний источник данных, а не часть React, и знаете ли правила работы с ним: где разрешены побочные эффекты, зачем нужна отписка и почему нельзя читать изменяемое значение прямо в теле рендера.
Как отвечать
Разберите вопрос по причинам, потому что за одной формулировкой прячется несколько разных ошибок.
- Компонент оказался вне провайдера, и хуки не находят контекст хранилища. Частый случай: провайдер обёрнут вокруг части дерева, а модальное окно рендерится в портал из другого корня.
- Круговой импорт: модуль хранилища тянет редьюсеры, редьюсер тянет модуль хранилища, и в момент выполнения переменная ещё не определена. В консоли это выглядит как обращение к
getStateуundefined. - Подписка сделана в теле компонента. Тогда она создаётся заново на каждый рендер и никогда не снимается.
Правильная форма ручной подписки такая:
useEffect(() => store.subscribe(handleChange), []);Функция, возвращённая из эффекта, снимает подписку при размонтировании, а subscribe как раз возвращает нужную функцию. В современном React для внешних хранилищ есть useSyncExternalStore, который согласует снимок с рендером; в связке с Redux достаточно useSelector, он построен на том же механизме.
Где спотыкаются
Подписываются без отписки. После размонтирования колбэк продолжает работать и трогать состояние удалённого компонента, а на каждом переходе между страницами подписок становится больше.
Читают состояние вызовом getState в теле компонента и ждут обновлений. Это разовый снимок, React о последующих изменениях не узнаёт и перерисовку не запланирует, поэтому нужен селектор или хук подписки.
Винят строгий режим за то, что эффект выполняется дважды. Двойное монтирование в разработке сделано намеренно и показывает ровно один дефект: эффект не умеет убирать за собой. Отключение строгого режима прячет симптом и оставляет утечку на месте.