Матрица публичного API
hummbit
getState(): Readonly<RootState>setState(input: RootState | NextStateUpdater<RootState>): Promise<void>mergeState(input: Patch<RootState> | Updater<RootState>): Promise<void>configureGlobalStore(config: GlobalStoreConfig): voidselector<T>(selectorFn: (state: Readonly<RootState>) => T): TinitStore(config, options?): InitializedStore<...>
hummbit/react
getState(): Readonly<RootState>setState(input: RootState | NextStateUpdater): Promise<void>mergeState(input: Patch | MergeUpdater): Promise<void>useSelector<T>(selectorFn, equalityFn?): TconfigureGlobalStore(config): voidinitStore(config, options?): InitializedReactStore<...>
Справочник API
hummbit.getState
Одноразовое чтение иммутабельного snapshot из глобального singleton store.
Базовое чтение
import { getState } from "hummbit";
const snapshot = getState();
console.log(snapshot);Чтение вложенного поля
import { getState } from "hummbit";
const userName = getState().user?.name ?? "anonymous";Чтение в обработчике события
import { getState } from "hummbit";
function onCheckoutClick() {
const cart = getState().cart;
submitCheckout(cart);
}Чтение после awaited обновления
import { setState, getState } from "hummbit";
await setState((prev) => ({ ...prev, ready: true }));
console.log(getState().ready); // trueАнти-паттерн и корректный подход
import { getState } from "hummbit";
// Анти-паттерн: считать, что значение всегда актуально
const cached = getState().counter;
// Правильно: читать getState() каждый раз при необходимости
const fresh = getState().counter;hummbit.setState
Полная замена корневого state глобального store.
Прямая замена объектом
import { setState } from "hummbit";
await setState({ user: { id: "u1" }, ready: true });Замена через updater
import { setState } from "hummbit";
await setState((prev) => ({ ...prev, ready: !prev.ready }));Асинхронный updater
import { setState } from "hummbit";
await setState(async (prev) => {
const profile = await loadProfile();
return { ...prev, profile };
});Порядок вызовов
import { setState } from "hummbit";
const p1 = setState((s) => ({ ...s, step: 1 }));
const p2 = setState((s) => ({ ...s, step: 2 }));
await Promise.all([p1, p2]); // применяется в порядке вызоваАнти-паттерн и корректный подход
import { setState } from "hummbit";
// Анти-паттерн: потерять остальные поля корня
await setState({ user: { id: "u2" } } as any);
// Правильно: сохранить root через spread
await setState((prev) => ({ ...prev, user: { ...prev.user, id: "u2" } }));hummbit.mergeState
Поверхностный patch на уровне корня.
Root patch
import { mergeState } from "hummbit";
await mergeState({ loading: true });Patch через updater
import { mergeState } from "hummbit";
await mergeState((prev) => ({ counter: prev.counter + 1 }));Асинхронный patch
import { mergeState } from "hummbit";
await mergeState(async () => {
const token = await refreshToken();
return { token };
});Статусы запроса
import { mergeState } from "hummbit";
await mergeState({ requestStatus: "pending" });
await mergeState({ requestStatus: "done" });Анти-паттерн и корректный deep update
import { mergeState } from "hummbit";
// Анти-паттерн: перезаписать весь user
await mergeState({ user: { age: 31 } as any });
// Правильно:
await mergeState((prev) => ({ user: { ...prev.user, age: 31 } }));hummbit.configureGlobalStore
Конфигурация глобального singleton для DevTools и middleware.
Выключить DevTools
import { configureGlobalStore } from "hummbit";
configureGlobalStore({ devtools: false });Включить с кастомным именем
import { configureGlobalStore } from "hummbit";
configureGlobalStore({ devtools: { enabled: true, name: "app-global" } });Добавить middleware
import { configureGlobalStore, type Middleware } from "hummbit";
type Root = { count: number };
const logger: Middleware<Root> = (ctx) => ({
afterUpdate: () => console.log("count", ctx.getState().count),
});
configureGlobalStore({ middleware: [logger] });Инициализация на старте
import { configureGlobalStore } from "hummbit";
export function bootstrap() {
configureGlobalStore({ devtools: true });
}Анти-паттерн и корректный подход
import { configureGlobalStore } from "hummbit";
// Анти-паттерн: поздняя пере-конфигурация
setTimeout(() => configureGlobalStore({ devtools: false }), 5000);
// Правильно: один вызов при bootstrap
configureGlobalStore({ devtools: false });hummbit.selector
Одноразовый селектор для глобального state (не подписка).
Базовое чтение
import { selector } from "hummbit";
const isAuth = selector((s) => Boolean(s.session?.token));Вычисляемое значение
import { selector } from "hummbit";
const total = selector((s) => s.items.reduce((acc, i) => acc + i.price, 0));Optional chaining
import { selector } from "hummbit";
const city = selector((s) => s.user?.address?.city ?? "unknown");Guard перед сайд-эффектом
import { selector } from "hummbit";
if (selector((s) => s.flags?.canUpload)) {
startUpload();
}Анти-паттерн и корректный подход
import { selector } from "hummbit";
// Анти-паттерн: воспринимать selector как реактивную подписку
const value = selector((s) => s.counter);
// Правильно: читать снова после обновлений или useSelector в React
const next = selector((s) => s.counter);hummbit.initStore
Создаёт изолированный типизированный store с actions, selectors, select.
Минимальный store
import { initStore } from "hummbit";
const store = initStore({
initialState: { count: 0 },
actions: ({ actionCreator, setState }) => ({
inc: actionCreator("inc", () =>
setState((s) => ({ ...s, count: s.count + 1 })),
),
}),
selectors: { count: (s) => s.count },
});Использование select(...)
const count = store.select(store.selectors.count);freeze: false
import { initStore } from "hummbit";
const store = initStore({
initialState: { debug: true },
freeze: false,
actions: () => ({}),
selectors: { debug: (s) => s.debug },
});Опции DevTools (второй аргумент initStore)
import { initStore } from "hummbit";
const store = initStore(
{
initialState: { q: 0 },
devtools: { name: "query-store" },
actions: () => ({}),
selectors: { q: (s) => s.q },
},
{ devtools: { hideSetState: true } },
);Что важно понимать:
config.devtools(первый аргумент) отвечает за включение/выключение интеграции DevTools.options.devtools(второй аргумент) отвечает за тонкую настройку логирования/имени.- Это разные уровни настройки и они работают вместе.
Полный список параметров второго аргумента:
type InitStoreOptions = {
devtools?: {
name?: string;
hideSetState?: boolean;
hideMergeState?: boolean;
};
};Расшифровка:
options.devtools.name— имя инстанса store в Redux DevTools.
Приоритет выше, чем уconfig.devtools.name.options.devtools.hideSetState— скрывает событияsetStateв DevTools-логе (на поведение store не влияет).options.devtools.hideMergeState— скрывает событияmergeStateв DevTools-логе (на поведение store не влияет).
Анти-паттерн и корректный подход
// Анти-паттерн: держать всю типизацию только в глобальном RootState
// Правильно: предпочитать локальные типы через initStorehummbit/react.getState
Семантика как у hummbit.getState, но типы идут через публичный augmentable RootState.
Базовое чтение
import { getState } from "hummbit/react";
const st = getState();One-off проверка для UI-команды
import { getState } from "hummbit/react";
function onOpenModal() {
if (getState().permissions?.canOpen) openModal();
}Read в аналитике
import { getState } from "hummbit/react";
track("page_open", { locale: getState().locale });Await и затем read
import { setState, getState } from "hummbit/react";
await setState((s) => ({ ...s, hydrated: true }));
console.log(getState().hydrated);Анти-паттерн и корректный подход
// Анти-паттерн: использовать getState() как механизм постоянного рендера
// Правильно: для реактивного UI использовать useSelector(...)hummbit/react.setState
Полная замена root state из React entrypoint.
Переключение флага
import { setState } from "hummbit/react";
await setState((s) => ({ ...s, open: !s.open }));Async интеграция с fetch
import { setState } from "hummbit/react";
await setState(async (s) => ({ ...s, profile: await fetchProfile() }));Loading lifecycle
import { setState } from "hummbit/react";
await setState((s) => ({ ...s, loading: true }));
await setState((s) => ({ ...s, loading: false }));Event callback update
import { setState } from "hummbit/react";
const onDismiss = () => setState((s) => ({ ...s, toast: null }));Анти-паттерн и корректный подход
// Анти-паттерн: мутировать старый объект и возвращать его же
// Правильно: всегда возвращать новый root-объектhummbit/react.mergeState
Поверхностный merge на уровне корня из React entrypoint.
Флаг загрузки
import { mergeState } from "hummbit/react";
await mergeState({ loading: true });Updater patch
import { mergeState } from "hummbit/react";
await mergeState((s) => ({ counter: s.counter + 1 }));Async patch
import { mergeState } from "hummbit/react";
await mergeState(async () => ({ token: await refreshToken() }));Step update
import { mergeState } from "hummbit/react";
await mergeState({ step: 2 });Анти-паттерн и корректный подход
// Анти-паттерн: ожидать deep merge
// Правильно: обновлять вложенные поля вручную через spreadhummbit/react.useSelector
Подписывает React-компонент на slice глобального singleton store.
Базовый вариант
import { useSelector } from "hummbit/react";
function Counter() {
const value = useSelector((s) => s.counter);
return <span>{value}</span>;
}Кастомный equality
import { useSelector } from "hummbit/react";
const id = useSelector(
(s) => s.user.id,
(a, b) => a === b,
);Derived selector
import { useSelector } from "hummbit/react";
const total = useSelector((s) => s.items.reduce((acc, i) => acc + i.qty, 0));Разделение селекторов для perf
const name = useSelector((s) => s.user.name);
const age = useSelector((s) => s.user.age);Анти-паттерн и корректный подход
// Анти-паттерн: всегда новый объект без equality
const userView = useSelector((s) => ({ id: s.user.id, name: s.user.name }));
// Правильно: добавить equality или выбирать примитивы отдельноhummbit/react.configureGlobalStore
Та же функция конфигурации глобального store, экспортируется и в React entrypoint.
Выключить DevTools
import { configureGlobalStore } from "hummbit/react";
configureGlobalStore({ devtools: false });Кастомное имя DevTools
configureGlobalStore({ devtools: { enabled: true, name: "react-global" } });Middleware
import { configureGlobalStore, type Middleware } from "hummbit/react";
const mw: Middleware<any> = () => ({
afterUpdate: () => console.log("updated"),
});
configureGlobalStore({ middleware: [mw] });Bootstrap
export const setupStore = () => {
configureGlobalStore({ devtools: true });
};Анти-паттерн и корректный подход
// Анти-паттерн: пере-конфигурировать в компонентах на каждом рендере
// Правильно: вызывать один раз в bootstraphummbit/react.initStore
Создаёт instance-store и добавляет store.useSelector.
Базовый React store
import { initStore } from "hummbit/react";
export const store = initStore({
initialState: { count: 0 },
actions: ({ actionCreator, setState }) => ({
inc: actionCreator("inc", () =>
setState((s) => ({ ...s, count: s.count + 1 })),
),
}),
selectors: { count: (s) => s.count },
});Потребление через store.useSelector
function Counter() {
const count = store.useSelector(store.selectors.count);
return <button onClick={() => store.actions.inc()}>{count}</button>;
}Кастомный equality
const profile = store.useSelector(
(s) => s.profile,
(a, b) => a.id === b.id && a.version === b.version,
);Async action flow
const asyncStore = initStore({
initialState: { loading: false, data: null as null | string },
actions: ({ actionCreator, setState }) => ({
load: actionCreator("load", async () => {
await setState((s) => ({ ...s, loading: true }));
const data = await Promise.resolve("ok");
await setState((s) => ({ ...s, loading: false, data }));
}),
}),
selectors: { data: (s) => s.data, loading: (s) => s.loading },
});Анти-паттерн и корректный подход
// Анти-паттерн: держать shared domain logic только в локальном component state
// Правильно: выносить shared updates/actions в initStore
