Комментарии 23
Теперь по апи пробежимся. Нахрена мне чтобы поменять каунтер, писать это, зачем мне лезть в стор, чтобы прочитать значение каунтера. зачем эти лишние обертки. Тут три строки кода, а это уже больно читать.
это следствие того, что здесь архитектурно атомы и непосредственно их данные разделены. Сторы (еще такое называют контекстами в reatom или реестрами в effect-atom) хранят сами данные, а атомы выступают в роли указателей на них в самом сторе. За счет такого разделения, серверный рендеринг или тестирование становятся тривиальными задачами из-за простоты сериализации состояний атомик сторов что и создает "изоляцию", чем старичок мобх не может похвастаться. Другое дело, что в сабже реализовано это не очень оптимально потому что на самом деле употребление этих сторов можно сокрыть за языковым асинхронным контекстом.
А, ну и в сабже даже встроенная работа с асинхронностью есть. А что там у мобх? Да ничего нет, для нормальных ленивой асинхронности нужно на коленке строить модели через всякие createAtom/reportChanged, а onBecomeObserved так вообще с багой про гонки состояний до сих пор как-то существует
Мне как пользователю библиотеки на самом деле без разницы, какая архитектура не позволила авторам библиотеки реализовать нормальное апи, я просто буду использовать ту библиотеку, которую удобно использовать.
это не так работает. Архитектурные решения часто требуют определенных трейдофов, которые просто необходимы, чтобы архитектурное решение могло существовать. Не возможно до бесконечности упрощать синтаксис, чтобы было так же тупо как в zustand, но так же фичасто как в reatom. Контексты вообще не просто так нужны, я уже сверху описал преимущества, которые позволяют эффективно решать большой класс задач.
И да, reatom даже тут может похвастаться тем, что сниппет кода сверху можно переписать вот так:
console.log(countAtom()) // 0
countAtom.set(5)
контекста в написании этого кода нет, а на самом деле он есть и поэтому он не отвлекает.
Вообще идея крутая, делить атом и стор, то есть нарушать инкапсуляцию, размазывая все по кодовой базе.
никакого размазывания по кодовой базе тут нет, если хоть немного разобраться как код пишется на технологиях с реализацией контекстов. Код пишется так будто бы данные находятся в атомах и ты работаешь с данными как и везде, но технически данные плавно инъектируются во весь дата флоу подобно dependency injection
По поводу тестирования, есть класс, у класса есть конструктор, передаете туда моканые зависимости или немоканые и тестируете. Не понимаю как в этом может выигрывать реактивная библиотека
в том то и дело, что так любой код можно тестировать, проблема здесь во вложенных и взаимосвязанных между друг другом сторов. Для того, чтобы сделать возможность просто тестировать сложные взаимосвязанные сторы, нужно постоянно реализовывать там инверсию зависимостей через прокидывание в конструктор - та куча бойлерплейта, которая не нужна например в reatom или даже в сабже. Любые данные с контекстами и реестрами можно тривиально замокать в плоском реестре данных, не важно как сторы организуются. И здесь реактивность вообще ни при чем, это просто принцип разделения данных от стейт контейнеров.
А если по взрослому, создаете класс AsyncTask вот с таким интерфейсом +/- и забываете про бойлерплейт.
а разве не заметно, что именно в этом и проблема? Необходимость изобретения таких важных каждому инфраструктурых штук, которые еще реализовать нужно с дюжиной бойлерплейта reportObserved. Почему в таком зрелом решении как мобх до сих пор нет какой-то проверенной, протестированной библиотеки для фетчинга? Это претензия не только к работе с асинхронностью в мобх, это претензия к экосистеме мобх в целом, потому что такая же проблема с формами, роутингом, персистентностью
createEtom/reportChanged использовать можно, но это какие-то очень специфичные кейсы.
ну, то есть запустит фетчинг автоматически, когда в компоненте пытаешься отрендерить его data, это специфичный кейс? Ну так, чисто чтобы не нужно было создавать кнопку fetch products при отображении списка товаров
onBecomeObserved что за бага про состояние гонки, нужен контекст или ссылка на issue на гитхабе. У меня проблем с ленивостью не возникло.
вот примитивнейший пример, при первой загрузке не рендерится isItemsFetching состояние, то есть фактически компонент показывает stale данные в момент первого рендеринга. Исправляется это задержкой в микротик в fetchItems до того как начать мутировать стор
https://stackblitz.com/edit/stackblitz-starters-dzte4xgd?file=src%2FApp.tsx
По поводу MobX дополню: в экосистеме есть инструменты вроде mobx-state-tree (сериализация, flow для асинхронки) и паттерны для ленивой загрузки, но вы правы — они требуют дополнительной настройки и не встроены «из коробки». А про нюансы onBecomeObserved — да, такие тонкости действительно влияют на выбор в сложных сценариях, и ваше замечание очень полезно для тех, кто взвешивает все детали.
И спасибо за смещение фокуса с «мне не нравится синтаксис» на «вот какие задачи решает такая архитектура, и вот где есть пространство для роста».
Привет! Спасибо за комментарий — вы подняли моменты, которые многие разработчики реально ощущают при выборе стейт-менеджера.
Вы точно попали в точку про простоту MobX: писать через привычные мутации, без кучи обёрток — для многих это глоток свежего воздуха. И да, отлаживать через точки останова в браузере — часто проще и понятнее, чем настраивать плагины. Особенно когда команда уже «в теме» и знает, как работает реактивность. Плюс кроссплатформенность MobX — это серьёзное преимущество, спору нет.
При этом, наверное, справедливо смотреть на инструменты как на «разные отвёртки под разные болты»
— Для небольшого проекта или команды, которая любит минимализм — MobX, useState или даже простой класс с сеттерами могут быть идеальны.
— Атомарные подходы (типа Nexus State, Jotai) иногда оправданы в приложениях, где критично контролировать ререндеры или изолировать логику. Но вы правы: для счётчика писать store.set(countAtom, 5) — это явный перебор.
Главное — не абсолютизировать ни один подход. У каждого решения есть своя аудитория, свои задачи и компромиссы. Ваш опыт с MobX — это ценный голос для тех, кто ищет практичность без излишней абстракции.
Можно пару строк о сравнении с Zustand?
Философия
— Zustand: одно хранилище, минимум абстракций. Быстро, просто, «меньше магии».
— Nexus State: состояние как сеть независимых атомов. Изоляция, явные зависимости, предсказуемость в сложных сценариях.
Код в примерах
// Счётчик
// Zustand
const useCounter = create(set => ({ count: 0, inc: () => set(s => ({ count: s.count + 1 })) }));
// Nexus State
const countAtom = atom(0); // useAtom(countAtom) + setCount
// Асинхроника
// Zustand: ручное управление loading/error
fetchData: async () => { set({ loading: true }); ... }
// Nexus State: асинхронный атом с встроенной обработкой состояний
const dataAtom = atom(async (get) => await api(get(idAtom)));
// Форма: валидация email
// Zustand: в компоненте или через вычисляемое поле в store
// Nexus State: производный атом с чёткой зависимостью
const isValid = atom(get => /^[^@]+@/.test(get(emailAtom)));
О формах
— Zustand: удобно для простых форм. Легко сбросить всё состояние. Отлично дружит с react-hook-form. Валидация — в логике компонента или через доп. поля.
— Nexus State: каждое поле — отдельный атом. Обновление имени не триггерит ререндер email-поля. Идеально для динамических форм, кросс-валидаций, глубокой изоляции. Сериализация для SSR — встроенная (store.extract()).
Итог
— Выбираете скорость и простоту? → Zustand.
— Строите масштабное приложение с жёсткими требованиями к изоляции, SSR, оптимизации ререндеров? → Атомарный подход.
Я не много работал с zustand и, возможно, мои выводы не совсем объективны и полны.
В любом случае - спасибо за вопрос )
Визуально похоже на reatom и zustand.
Не надо грязи, в реатоме такого неудачного АПИ нет.
Не исключено, что промпт так и звучал: напиши библиотеку типа zustand с фичами из reatom.
Ну что ж
Каунтер на моле , где компоненты сами отслеживают состояние без помощи внешних библиотек, в студию !
# counter.view.tree
$my_counter $mol_view
sub /
<= Count $mol_number
value? <=> count?
<= Plus $mol_button
title \+
click? => inc?
// counter.view.ts
namespace $.$$ {
export class $my_counter extends $.$my_counter {
@ $mol_mem
count( next?: number ) {
return next ?? 0
}
inc() {
this.count( this.count() + 1 )
}
}
}



Nexus State: Современный менеджер состояния для JavaScript-приложений