Comments 8
Важное отличие "redux-подобного" решения от redux: redux не хранит store в react state. Благодаря этому возможно спокойно читать и менять store вне react компонентов (например, в сервисном слое приложения)
Не рекомендую использовать такое решение в реальных проектах. Во всяком случае в больших:
- Нет мемоизации, если над provider-ом что-то есть, то каждый случайный render provider-а приведёт к rerender-у всех consumer-ов. Мелочь, но неприятно, лечится мемоизацией
- Вы храните store в context-е. По сути вы используете context-механизм React-а для обеспечения реактивности. Да, это самое простое решение, но с таким подходом вы не сможете реализовать useSelector и подобные хуки так, чтобы они были производительными.
На втором пункте остановлюсь подробнее. Любое изменение Provider.value приводит к рендеру всех consumer-ов. А если consumer-ы рендерят что-нибудь не PureComputed || Memo || ..., то это срабатывает ещё каскадом и на дочерние компоненты. А это большая нагрузка. Много лишней работы.
Посудите сами, у вас ваш глобальный store слушает множество потребителей. И каждому нужен какой-нибудь свой кусок store. Но rerender они будут получать всегда все. В случае react-redux/connect у вас всегда вызываются все mapStateToProps. Это "болезненно" иногда, но всё же не те масштабы, что в случае вашего useStore.
И вы никак не сможете реализовать хук производительным образом пока не откажетесь от хранения store-данных в контексте. А если откажетесь — то вам придётся писать свою собственную модель подписок\реактивности. Во всех решениях что я смотрел, так и поступили. Самое интересное вместо реальных store данных возвращает proxy-обёртку, которая детектит все обращения к ней и записывает их, а при следующем рендере сверяет только те поля, которые были изменены в store data.
Правда можно отказаться от хуков и всегда использовать HoC вроде connect-а. Тогда, кажется, проблем нет.
Спасибо за комментарий.
В комментариях под оригинальной статьей есть вопрос по схожей тематике и ответ автора статьи. Ниже приведу перевод этого диалога:
Вопрос:
Эй! У меня есть вопрос. Мне действительно нравится это решение и я реализовал что-то похожее в своем приложении, но я заметил такую вещь. Из-за того, что я использую
useState в множестве различных компонентов, он вызывается множество раз при загрузке. Думаете ли вы, что это будет серьезным препятствием при масштабировании или есть
путь реализовать это таким образом, что бы он не вызывался снова и снова для извлечения значений?
Ответ:
Привет, Брендон! На работе в продакшене мы переместили dispatch в отдельный контекст и теперь имеем отдельные useDispatch и useStore хуки. Обычно компоненты, которым нужен
dispatch actions, в основном, не используют само состояние. Этот момент сможет срезать большое количество ререндеров, которые вы наблюдаете.
Другой опцией для возможного сокращения вызовов может быть создание "hydrate store" action, который будет совершать всю начальную загрузку состояния, которое Вам нужно в
одном действии. Я не уверен, насколько это возможно в вашем проекте, но мы тоже этим занимаемся.
Всё равно остаётся вопрос как он реализовал useStore. Если просто вынес его в отдельный контекст, то толку с этого не сильно много :) Т.к. по сути ничего не изменилось.
Глубоко не копал, но судя по вот этому коду, там внутри используется своя система подписок, а обновление осуществляется за счёт setState(newObject) (forceRerender по сути).
Ну т.е. как и у всех. Никаких проблем не вижу.
Создание Redux-подобного глобального хранилища, используя React Hooks