Компонент сам себе стор, а внешний стор это антипаттерн

Здравствуйте, товарищи! Эта статья про state management во фронтенде, с непривычного угла. За два года я ни разу не написал ни одного редьюсера. Расскажу, как я к этому пришёл, что слышу со стороны про сторы, и почему вам, особенно если вы только заходите во фронтенд, не надо учить Redux в принципе.

Сначала немного о себе, потом про то, что такое стор и почему я считаю его антипаттерном. Затем покажу, как без него живёт моё реальное приложение. И разберем возражения

Пара слов о себе

Меня зовут Кирилл. Пишу на $mol, это такой реактивный фреймворк, который сделал @nin-jin. Когда‑то давно я работал на Vue 2 ( месяца два ), без всякого стора, компонент, props, эмитов. Vue не учил, просто правил баги на сайте.

Не так давно переехал в питер, начал ходить на митапы, и увидел статьи Димы Карловского, ну и лично с ним познакомился тоже. Меня сильно зацепило то что в его статьях есть глубокое понимание, а главное — технические анализы ( чего больше нигде в индустрии нет ).

Решился попробовать $mol. Далеко не с первого раза понял, как читать, и что такое вообще view.tree ( это абстракция над html, которая транспилируется в самые обыкновенные js классы ). В общении со знакомыми фронтами замечаю, что концепции, на которые они жалуются, в $mol не существуют. Не «решены проще», а физически не нужны.

Стор одна из таких концепций. Я считайте что сразу начал писать на $mol«e и поэтому не набрался вредных привычек не написал ни одного dispatch. И ничего не потерял.»

Стор глазами стороннего наблюдателя

Я состою в чатике с фронтами, они там в основном используют React, Angular и хвалят Vue)

Пару цитат оттуда:

Мы используем редакс, у нас стор хранит в себе все данные приложения, и при запуске devtools он падает, потому что не смог через девтулзы пропустить всё то говно, которое они туда накидали.

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

Я не могу себя заставить на интеграционные перейти, так как мокать кучу рестов, кучу изменений стора, короче, пипец.

Советский балкон хаххахаха Типичный Redux‑стор превращается в советский балкон. Туда сваливается всё подряд.

Если посмотреть в стейт типичного Redux‑приложения, то выглядит это примерно так:

{
    auth:     { ... },
    entities: {
        posts:    { byId, allIds },
        users:    { byId, allIds },
        comments: { byId, allIds },
    },
    ui: {
        sidebar:   { open: false },
        modal:     { type: null, payload: null },
        forms:     { register: { email: '', password: '' } },
        hover:     { itemId: null },         // ← hover, серьёзно
    },
    pagination: { posts: { page: 1, total: 50 } },
    cache:      { ... },                      // ← RTK Query
}

Hover лежит в глобальном сторе рядом с access‑токеном пользователя. Состояние формы регистрации тоже там. Всё в куче опять же. Парировать бардак тем что «это задача програмиста не устраивать бардак» не стоит, если так делают значит так делать проще, и так будут делать всегда. Технология должна упрощать хорошие практики, и делать затруднительными плохие, а не наоборот.

Зачем вообще нужен стор? Компонент же сам должен хранить своё состояние.

Был на «Фронтенд Подлодке», смотрел лекцию про React, возник вопросец:

В реакте нужно вручную вызывать функцию, чтобы отрендерить ответ с бэка?

Ответ был, да, в общем‑то, нужно. То есть в React компонент сам по себе не знает, что данные изменились. Ему надо это сказать через setState, или через подписку на стор, или через хук типа useQuery. Если вы записали значение в обычное поле объекта, React этого не заметит, ререндера не будет.

В React«e нет встроенной реактивности. То есть стор это костыль для отсутствующей фичи фреймворка. Беря React, вы собираете свой костыльный фреймворк, получается.»

Vue в этом смысле умнее, там есть ref и reactive, прикрученные прямо к фреймворку. Но Vue‑комьюнити всё равно к этому добавило Vuex, потом Pinia, те же сторы, только сбоку. Зачем?

В $mol всё иначе. Каждое поле, обёрнутое в @$mol_mem, автоматически уведомляет компоненты, которые его читают. Изменил, и DOM обновился. Без подписок, без диспатчей, без хуков. Так должна выглядеть «реактивность из коробки».

Eщё цитата:

Я только щас понял что [коллега] написал, data из квери‑хука не будет в сторе храниться, пока ты туда не засунешь вручную.

То есть есть Redux‑стор, есть useQuery из TanStack, и они не связаны между собой. Запрос сходил на бэк, данные пришли, кэш TanStack‑а их сохранил, но в Redux они не попадут, пока вы их туда руками не положите. И в Redux‑комьюнити это норма.

Я уточнил:

Сейчас стандарт кэш хранить в редакс, так как 99% юзают RTK query или react query.

То есть сам Redux уже не справляется, все используют RTK Query или TanStack Query, потому что в чистом Redux работать с асинхронкой больно. И теперь у вас:

  1. Redux для глобального состояния

  2. RTK Query / TanStack Query для серверного кэша

  3. useState для локального компонентного состояния

  4. useReducer какой нибудь для среднего, между локальным и глобальным

  5. Context для прокидывания мимо props

Пять подсистем хранения состояния в одном приложении, и в каждой свои правила, API, паттерны. И разработчик каждый раз решает, куда положить очередную переменную.

У меня в $mol одна подсистема, реактивные поля компонента, и всё. Локальное лежит в компоненте, общее поднимается в общего предка, глобальное в синглтоне App. Где живёт состояние, диктует дерево компонентов, а не выбор библиотеки.

Недавно я запилил проект Blitz, это аналог Kahoot, онлайн квиз в реальном времени. Хост заводит сессию, игроки заходят по коду, отвечают на вопросы, видят баллы в реальном времени. Без бэкенда, всё через Giper Bazy, которая сама синкает данные между устройствами.

Там реализовано: блог, профили, лобби, мультиплеер, реакции. По хорошему, поле для стора огромное. В React‑варианте этого приложения был бы солидный slice на сессию, slice на игроков, slice на текущий вопрос, slice на UI, slice на профиль, плюс RTK Query на серверные данные. Я думаю, файлов было бы под сотню.

Но у меня сторов нет.

Вот, например, как описан игрок:

export class $bog_blitz_player extends $giper_baza_dict.with({
	Score: $giper_baza_atom_real,
	Name: $giper_baza_atom_text,
	IsHost: $giper_baza_atom_bool,
	Avatar: $giper_baza_atom_link_to(() => $giper_baza_file),
	Answer_land: $giper_baza_atom_text,
}) {}

Это схема данных. Я говорю, игрок состоит из счёта, имени, флага «хост ли он», аватарки и ссылки на отдельный land с его ответами. Никаких actions, reducers, selectors, просто описание полей.

В коде я с ним работаю так:

const player = $bog_blitz_player.of(player_id)

player.Name() // ← чтение имени
player.Score(42) // ← запись счёта
player.Score() // ← чтение счёта
player.IsHost() // ← реактивная подписка на флаг

Хост на одном устройстве пишет player.Score( 42 ). Через секунду на устройстве игрока обновляется счёт сам, потому что Giper Baza засинкала изменение, а $mol узнал что данные поменялись и перерендерил view. Никакого диспатча, никакого socket.on('score-update', ...), никакой ручной синхронизации.

А вот так устроен переход между экранами:

screen( next?: string ) {
    if( next !== undefined ) {
        this.mobile_menu_showed( false )
        if( next === 'lobby' ) {
            this.$.$mol_state_arg.value( 'quiz', null )
        }
    }
    return this.$.$mol_state_arg.value( 'screen', next || undefined ) || 'admin'
}

$mol_state_arg это двусторонняя связь поля с query‑параметром URL.

Что у меня лежит локально в самих компонентах:

Всё это не существует на уровне общего стора. Оно живёт там, где используется. А когда компонент уничтожается, оно само себя удаляет.

Даже лучшим техническим решением сложно по‑настоящему искренне заинтересовать кого то, я пытался. Основная претензия, синтаксис view.tree:

Какой view.tree ужасный.

Это вообще не похоже на JSX.

Я не хочу учить новый DSL.

Я точно знаю что это дело привычки, не более. view.tree это отличное решение на самом деле, а JSX это HTML, прикрученный к JavaScript со всеми его недостатками.

Через две недели работы с view.tree вы перестаёте его замечать. Через месяц вы понимаете, что он строже JSX. Нельзя случайно засунуть в дерево что‑то лишнее, нельзя написать «логику в шаблоне», нельзя сделать typo в имени компонента, его проверит компилятор. Это тот же контраст, что между TypeScript и JavaScript: первое строже, второе свободнее, и через месяц TypeScript‑а вы не хотите обратно.

Ещё частые возражения:

Если вы только заходите во фронтенд, не учите Redux. Не учите Zustand, Pinia, Jotai, MobX, RTK Query, TanStack Query. Потому что если вы возьмёте $mol, вам не понадобится ничего из этого. Вы не будете писать редьюсеры, потому что компонент сам себе редьюсер. Вы не будете писать селекторы, потому что геттер компонента это и есть селектор. Вы не будете писать async middleware, потому что $mol_fetch работает синхронным кодом. Вы сэкономите примерно полгода обучения и потом будете писать в три раза меньше кода.

Мне повезло натолкнуться на $mol, я сразу начал с хороших решений и практик. Не научился плохому. И когда мои знакомые в чате жалуются на «советский балкон» в Redux‑сторе, на упавшие devtools, на муторные интеграционные тесты с моками, я раньше постоянно невпопад упоминал мол и всех раздражал:) Сейчас упоминают меня) Eще раз, тут не я молодец, просто фреймворк такой.

Если вы бывалый React‑разраб, и дочитали до этой строки, спасибо. Я не буду вас уговаривать переучиваться, потому что у вас есть проекты, команда, инерция, найм и прочая бытовая правда. Но на pet‑проект возьмите $mol на выходных, посмотрите, как это, когда стор не нужен. Может, понравится. К тому же у меня есть скил для нейронки, которая поможет разобраться:

npx skills add b-on-g/mol_skill --all -g

А если вы новичок, и фронтенд для вас ещё чистый лист, то всё ещё проще. Идите на mol.hyoo.ru, почитайте статьи Димы Карловского, приходите пообщаться на @PiterJS. И начните сразу с хорошего, как когда‑то начал я.

Спасибо за внимание, товарищи. Жду вопросы и возражения в комментариях.

@cmyser
04.06.2026 20:58 UTC
Первоисточник

Комментарии

@nektobit
04.06.2026 23:57 UTC
+2

Внешний стор используется для того чтобы шарить состояние между компонентами. Такая нужда возникает не часто, не вижу проблем с этим. Правда я пишу только на ng/vue, не знаю как там с реактом:) И да, я тоже не пишу редьюсеры, помимо redux есть жизнь.

@cmyser
05.06.2026 07:52 UTC
0

Ну я вижу два пути, либо в родительском компоненте хранить состояние и передавать в дочек, либо в урл ставить значение

Ну и конечно локал сторедж есть, стор уже четвертый лишний как будто

06.06.2026 15:19 UTC
+3

Либо ты просто джун и решил, что такое мнение кому-то надо

@unPayo
05.06.2026 07:47 UTC
+4

Более чем странная логика у автора статьи. Что-то типа - "Некоторые владельцы (не известное количество) балконов (хранилищ общих данных для нескольких компонент в одном месте) марки Redux захламляют их (кладут туда и локальные данные). Долой балконы как класс! Покупайте квартиры марки $mol со встроенными балконами."

Так претензии к балконам конкретной марки? К их некорректному использованию? К балконам в принципе? Но встроенный в $mol балкон не перестает быть балконом и не имеет защиты от некорректного использования.

P.S. Я тоже не вижу большого смысла в инструментах типа Redux, Vuex. Но, хотелось бы видеть аргументированную объективную техническую критику. А не рассуждения на тему, что кто-то кое-где у нас порой...

@cmyser
05.06.2026 07:54 UTC
0

Претензия к сторам что они поощряют "захламление"

В моле тоже можно сделать супер компонент и передавать его везде, но даже при написании такой мысли закрадываются сомнения о целесообразности данного решения)

05.06.2026 09:11 UTC
+3

А в чем тут могут быть сомнения?

Например, возьмем данные получаемые с сервера. А, как мне кажется, это львиная доля отображаемых данных в большинстве приложений. Одни и те же данные используются в разных компонентах. Вполне логично хранить "снимок" состояния данных сервера в одном месте в формате максимально приближенном к тому, в котором они приходят с сервера и с максимально мелкой степенью грануляции. И любой компонент может отображать нужные ему "гранулы" состояния сервера, свои локальные данные да и любую смесь из этих двух типов сделанную через какой-нибудь computed.

В данном случае абсолютно не важно как называется балкон инструмент. Лишь бы он обеспечивал необходимую функциональность. Я, например, строю такую схему на Mobx.

@Nomad-lab-sketch
05.06.2026 14:06 UTC
0

Я сам пришел к такой же модели, только реализация у меня своя. Так же scoped store который привязан к текущему месту использования. И если сам scop дестроится то и нет смысла хранить данные. Конечно могут быть ситуации когда реакт решит что надо компонент перерендерить и по итогу у нас заново структура хранения инициализируется и данные заново запрашиваются, это конечно не приятно. НО это заставляет правильно рендер оптимизировать что бы лишний раз не чего не пересоздавалось, но и тут есть цена.. в реакте управлять рендером напрямую нельзя к сожалению и сам реакт говорит "Не хочешь что бы я ререндерил узел? Выделяй память!". И это беда... т.к на высоконагруженных интерфейсах память может забиваться только так..

@cmyser
05.06.2026 14:33 UTC
0

О, на этот счёт могу посоветовать почитать вот эту статью https://habr.com/ru/articles/723728/

@ooko
06.06.2026 11:27 UTC
0

Не гонюсь за реактивностью. У меня ничего не отваливается когда "руками" сообщаю view что данные изменились и нужно проверить/перерисовать. Ну и данные храню там где хочу и как хочу. К примеру даже так var value = 67;

Хранить данные в компоненте нормальная практика. Только у React есть один минус - нельзя перенести компонент между родителями не потеряв стейт.

@Apolleon
06.06.2026 15:22 UTC
+1

Откуда вы такие берётесь?! Вас впринципе в команды работать не берут?!

06.06.2026 19:11 UTC
0

Я тут второй десяток лет как. Вы по существу что-то писать будете или так эмоции только?