javascript

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

  • понедельник, 16 февраля 2026 г. в 00:00:04
https://habr.com/ru/articles/996812/

В экосистеме 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 { atom } from '@nexus-state/core';

const [userAtom, fetchUser] = atom.async({
  fetchFn: async (store) => {
    const response = await fetch('/api/user');
    return response.json();
  }
});

// В компоненте
const [userState, setUserState] = useAtom(userAtom);

// userState содержит:
// { loading: boolean, error: Error | null, data: T | null }

// Запуск загрузки
fetchUser(store);

Пример 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

Синтаксис

Атомы: atom(0)

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)

  • Важны 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 оптимизирован для производительности:

  1. Селективные обновления — компоненты обновляются только при изменении связанных атомов

  2. Batched updates — несколько изменений в одной транзакции

  3. Lazy serialization — DevTools сериализует состояние только по запросу

  4. Minimal size — менее 2 Кб без gzip

  5. No overhead in production — DevTools код не включается в production сборку

Заключение

Nexus State — это современное решение для управления состоянием, которое сочетает минимализм с мощными функциями. Он особенно подходит для:

  • ✅ Новых проектов, где важна простота

  • ✅ Команд, ценящих производительность

  • ✅ Приложений, где важна отладка (DevTools)

  • ✅ Проектов, требующих Time Travel debugging

  • ✅ Микрофронтендов с изоляцией состояния

Библиотека активно развивается и уже демонстрирует отличные результаты в плане производительности и удобства использования.

Ресурсы