В экосистеме JavaScript-разработки управление состоянием приложений всегда оставалось одной из самых сложных задач. От глобальных переменных до сложных библиотек вроде Redux и MobX — разработчики постоянно ищут более простые и эффективные решения.
Сегодня мы познакомимся с Nexus State — новой библиотекой для управления состоянием, которая сочетает простоту использования Atom-подхода с мощными функциями для реальных приложений. В этой статье мы рассмотрим архитектуру Nexus State, его возможности, и проведем объективное сравнение с существующими решениями.
Что такое Nexus State?
Nexus State — это легковесная библиотека для управления состоянием, построенная на концепции атомов (atoms). Атомы — это минимальные единицы состояния, которые могут быть:
Примитивными — хранящими простые значения (строки, числа, булевы значения)
Вычисляемыми (computed) — производящими значения на основе других атомов
Асинхронными — управляющими состоянием запросов к API
Ключевые особенности:
✅ Простота и минимализм (менее 2 Кб в gzip)
✅ Built-in DevTools для отладки
✅ Time Travel debugging
✅ Поддержка React, Vue, Svelte и vanilla JS
✅ Плагины для persistence, middleware, async operations и Immer
✅ Автоматическая регистрация атомов для DevTools интеграции
Основные концепции
Атомы (Atoms)
Атом — это минимальная единица состояния. В Nexus State атом создается функцией atom():
import { atom, createStore } from '@nexus-state/core'; // Простой атом с начальным значением const countAtom = atom(0, 'counter'); // Вычисляемый атом на основе другого атома const doubleCountAtom = atom((get) => get(countAtom) * 2, 'doubleCount'); // Создаем store и используем атомы const store = createStore(); console.log(store.get(countAtom)); // 0 store.set(countAtom, 5); console.log(store.get(doubleCountAtom)); // 10
Вычисляемые атомы автоматически пересчитываются при изменении зависимых атомов — без лишних обновлений компонентов.
Store
Store — это контейнер для атомов. Он управляет подписками и обновлениями:
const store = createStore(); // Подписка на изменения const unsubscribe = store.subscribe(countAtom, (value) => { console.log('Count changed:', value); }); // Обновление store.set(countAtom, 10); // Отписка unsubscribe();
Практические примеры
Пример 1: Простой счетчик с React
import { useAtom } from '@nexus-state/react'; import { atom, createStore } from '@nexus-state/core'; const countAtom = atom(0, 'counter'); const doubleCountAtom = atom((get) => get(countAtom) * 2, 'doubleCount'); function Counter() { const [count, setCount] = useAtom(countAtom); const [doubleCount] = useAtom(doubleCountAtom); return ( <div> <h1>Count: {count}</h1> <p>Double: {doubleCount}</p> <button onClick={() => setCount(count + 1)}>+</button> </div> ); }
Важно: React-компоненты обновляются только когда меняются связанные атомы — никаких лишних ререндеров!
В приведенном ниже примере это можно увидеть в Computed Atoms
Пример 2: Асинхронные операции
import { asyncAtom } from '@nexus-state/async'; import { useAtom } from '@nexus-state/react'; import { createStore } from '@nexus-state/core'; // Создаем store const store = createStore(); // Создаем async atom для пользовательских данных const [userAtom, fetchUser] = asyncAtom({ fetchFn: async (store) => { // Симулируем API запрос с задержкой await new Promise(resolve => setTimeout(resolve, 1000)); // Возвращаем данные пользователя return { id: 1, name: 'John Doe', email: 'john.doe@example.com', age: 30 }; } }); // В компоненте function UserProfile() { const [userState] = useAtom(userAtom, store); // userState содержит: // { loading: boolean, error: Error | null, data: T | null } return ( <div> {userState.loading && <p>Loading...</p>} {userState.error && <p>Error: {userState.error.message}</p>} {userState.data && ( <div> <h3>{userState.data.name}</h3> <p>Email: {userState.data.email}</p> <p>Age: {userState.data.age}</p> </div> )} <button onClick={() => fetchUser(store)}> Load User Data </button> </div> ); }
Пример 3: Family (параметризованные атомы)
import { atomFamily } from '@nexus-state/family'; const userAtomFamily = atomFamily((id) => atom({ id, name: '', email: '' }) ); // Получаем атом для конкретного пользователя const user1Atom = userAtomFamily(1); const user2Atom = userAtomFamily(2); store.set(user1Atom, { id: 1, name: 'Alice', email: 'alice@example.com' }); store.set(user2Atom, { id: 2, name: 'Bob', email: 'bob@example.com' });
Пример 4: Persistence (сохранение в localStorage)
import { persist, localStorageStorage } from '@nexus-state/persist'; const store = createStore(); // Применяем плагин persistence persist(countAtom, { key: 'count', storage: localStorageStorage })(store); // Теперь значение сохраняется автоматически и восстанавливается при перезагрузке store.set(countAtom, 42); // Обновите страницу — значение останется 42!
Пример 5: Middleware
import { middleware } from '@nexus-state/middleware'; // Логирование изменений const loggingMiddleware = middleware(countAtom, { beforeSet: (atom, newValue) => { console.log('Will set:', newValue); return newValue; }, afterSet: (atom, newValue) => { console.log('Did set:', newValue); } }); store = createStore([loggingMiddleware]);
Пример 6: Immer для сложного состояния
import { immerAtom, setImmer } from '@nexus-state/immer'; const userStateAtom = immerAtom({ profile: { name: '', age: 0 }, preferences: { theme: 'light' } }, store); // Обновление вложенных свойств с мутациями setImmer(userStateAtom, (draft) => { draft.profile.name = 'Jane'; draft.preferences.theme = 'dark'; // Draft автоматически становится новым объектом });
DevTools для отладки
Nexus State включает полноценные DevTools, которые позволяют:
🕵️ Inspect все атомы и их значения
⏪ Time Travel — перемотка между состояниями
📊 Просмотр истории изменений
🔄 Восстановление состояния из снапшотов
import { devTools } from '@nexus-state/devtools'; const store = createStore(); const devtoolsPlugin = devTools(); devtoolsPlugin.apply(store);
Сравнение с другими решениями
Nexus State vs Redux
Критерий | Nexus State | Redux |
|---|---|---|
Синтаксис | Атомы: | Action types, reducers |
Boilerplate | Минимальный | Значительный |
Типизация | Встроенная | Через typescript |
DevTools | Встроены | Redux DevTools extension |
Size | ~2 Кб | ~13 Кб |
Learning curve | Плоская | Крутая |
Computed values | Автоматически | Через selector |
Async | Встроенный asyncAtom | Через middleware (thunk/saga) |
Когда выбрать Redux:
Большие команды, привыкшие к паттерну Redux
Необходимость строгой структуры и конвенций
Исторические причины (множество Redux кода)
Когда выбрать Nexus State:
Желание минимализма и простоты
Нужна быстрая разработка
Требуются вычисляемые значения "из коробки"
Важна интеграция с DevTools
Nexus State vs Zustand
Критерий | Nexus State | Zustand |
|---|---|---|
Парадигма | Атомы | Store функция |
Computed values | Встроены | Через get() |
DevTools | Встроены | Плагин |
Size | ~2 Кб | ~1.5 Кб |
React integration | useAtom | useStore |
Time Travel | Встроен | Не встроен |
Family support | Встроен | Плагин |
Zustand предпочтительнее если:
Нужен самый маленький размер
Используете только React
Не нужен Time Travel
Nexus State предпочтительнее если:
Нужна поддержка Vue/Svelte
Важны DevTools и Time Travel
Требуется family support
Nexus State vs MobX
Критерий | Nexus State | MobX |
|---|---|---|
Парадигма | Атомы | Observable |
Обновления | Селективные | Автоматические |
Транзакции | Не требуется | Встроены |
DevTools | Встроены | mobx-devtools |
Size | ~2 Кб | ~10 Кб |
Learning curve | Легкая | Средняя |
MobX предпочтительнее если:
Уже используете MobX в проекте
Нужны транзакции "из коробки"
Предпочтителен observable подход
Nexus State предпочтительнее если:
Нужна более явная модель изменений
Важна интеграция с DevTools и Time Travel
Требуется поддержка нескольких фреймворков
Nexus State vs Jotai
Критерий | Nexus State | Jotai |
|---|---|---|
Архитектура | Store + Atoms | Global atoms |
Store | Встроен | Пользовательский |
Isolation | Хорошая | Через Provider |
DevTools | Встроены | Нужен плагин |
Computed | Встроены | Встроены |
Size | ~2 Кб | ~3 Кб |
Jotai предпочтительнее если:
Нужен глобальный store
Не нужна изоляция
Предпочтителен simpler API
Nexus State предпочтительнее если:
Нужна изоляция ( несколько store instances)
Важны DevTools и Time Travel
Требуется встроенный persistence
Архитектура и внутреннее устройство
Atom Registry
Все атомы автоматически регистрируются в глобальном реестре:
const atomRegistry = { get: (id: symbol) => Atom, getAll: () => Atom[], getName: (atom: Atom) => string }
Это позволяет DevTools отображать все атомы и их зависимости.
Computed Atoms
Вычисляемые атомы не хранят значение — они вычисляются на лету:
const a = atom(1); const b = atom(2); const c = atom((get) => get(a) + get(b)); // При изменении 'a' или 'b', 'c' автоматически пересчитывается // Компоненты, зависящие от 'c', обновляются только при изменении 'c'
Batched Updates
Nexus State автоматически батчит изменения:
store.set(a, 1); store.set(b, 2); // Компоненты обновятся только один раз после всех изменений
Time Travel
Система Time Travel использует снапшоты состояния:
const snapshotManager = new StateSnapshotManager(atomRegistry); const stateRestorer = new StateRestorer(atomRegistry); // Создать снапшот const snapshot = snapshotManager.createSnapshot('USER_ACTION'); // Восстановить состояние stateRestorer.restoreFromSnapshot(snapshot);
Плагины и расширяемость
Nexus State поддерживает систему плагинов:
import { middleware } from '@nexus-state/middleware'; import { persist } from '@nexus-state/persist'; const store = createStore([ middleware(atom1, { beforeSet: ... }), persist(atom2, { key: 'key', storage: localStorage }), // ... другие плагины ]);
Плагины применяются к store через функции, которые принимают store и расширяют его функциональность.
Рекомендации по использованию
✅ Что делать:
Использовать вычисляемые атомы для производных данных
Делать атомы достаточно мелкими для точных обновлений
Использовать DevTools для отладки
Применять плагины для side effects (persistence, logging)
Использовать family для списков сущностей
❌ Что не делать:
Не хранить в атомах реактивные ссылки на DOM элементы
Не создавать слишком большие атомы (лучше мелкие и независимые)
Не использовать атомы для UI state (фокус, hover и т.д.)
Не забывать отписываться от store при размонтировании
Производительность
Nexus State оптимизирован для производительности:
Селективные обновления — компоненты обновляются только при изменении связанных атомов
Batched updates — несколько изменений в одной транзакции
Lazy serialization — DevTools сериализует состояние только по запросу
Minimal size — менее 2 Кб без gzip
No overhead in production — DevTools код не включается в production сборку
Заключение
Nexus State — это современное решение для управления состоянием, которое сочетает минимализм с мощными функциями. Он особенно подходит для:
✅ Новых проектов, где важна простота
✅ Команд, ценящих производительность
✅ Приложений, где важна отладка (DevTools)
✅ Проектов, требующих Time Travel debugging
✅ Микрофронтендов с изоляцией состояния
Библиотека активно развивается и уже демонстрирует отличные результаты в плане производительности и удобства использования.
