Slices
Compose multiple independent state shards into a single store, or use any shard standalone. Slice subscribers and combined-store subscribers stay in sync automatically — no matter which side wrote the change.
Basic Slices
Basic Slices
Two slices (auth, cart) composed into one combined store. Writes from either side flow to the other automatically.
{
"auth": {
"user": "Alice",
"role": "admin"
},
"cart": {
"items": 0,
"total": 0
}
}import { combineSlices, createHook, createSlice } from 'fluxo-ui/store';
const authSlice = createSlice<AuthState>('auth', () => ({ user: 'Alice', role: 'admin' }));
const cartSlice = createSlice<CartState>('cart', () => ({ items: 0, total: 0 }));
const appStore = combineSlices({ auth: authSlice, cart: cartSlice });
// Each slice has a hook of its own — selectors only see the slice's branch
const useAuth = createHook(authSlice);
const useCart = createHook(cartSlice);
// And the combined store has a hook for the whole tree
const useApp = createHook(appStore);
// Direct slice writes are reflected in the combined store and vice versa.
authSlice.setState({ role: 'editor' }); // appStore.getState().auth.role === 'editor'
appStore.setState((s) => ({ cart: { ...s.cart, items: s.cart.items + 1 } })); // cartSlice subscribers fire
Standalone Slice
Standalone Slice
A slice used on its own — works exactly like a regular Store<T>. No combined parent required.
import { createHook, createSlice } from 'fluxo-ui/store';
// A slice is a fully-functional store on its own — no combineSlices needed.
const timerSlice = createSlice<TimerState>('timer', () => ({ seconds: 0, running: false }));
const useTimer = createHook(timerSlice);
// Direct framework-agnostic usage from plain JavaScript:
timerSlice.on('change', 'seconds', (s) => console.log('seconds:', s.seconds));
timerSlice.setState({ running: true });
// Later — adopt the same slice into a combined store with combineSlices({ timer: timerSlice, ... })
// The slice keeps the same identity; existing subscribers keep firing.
Bidirectional Sync
Bidirectional Sync
Slice subscribers and combined-store subscribers both fire on every change, no matter which side wrote.
import { combineSlices, createSlice } from 'fluxo-ui/store';
const counterSlice = createSlice<CounterState>('counter', () => ({ value: 0 }));
const settingsSlice = createSlice<SettingsState>('settings', () => ({ theme: 'light', debug: false }));
const syncStore = combineSlices({ counter: counterSlice, settings: settingsSlice });
// Slice-level path subscription — only fires for this slice's branch
counterSlice.on('change', 'value', (s) => console.log('[counter slice] value =', s.value));
// Combined-level path subscription — fully-qualified path including the slice key
syncStore.on('change', 'counter.value', (s) => console.log('[combined] counter.value =', s.counter.value));
syncStore.on('change', 'settings.theme', (s) => console.log('[combined] settings.theme =', s.settings.theme));
// Either write style triggers BOTH subscriptions:
counterSlice.setState({ value: 1 }); // slice direct
syncStore.setState((s) => ({ counter: { ...s.counter, value: 2 } })); // combined direct
Framework-Agnostic Core
Framework-Agnostic Core
The slicing API is plain TypeScript. It runs in Node, web workers, plain DOM apps, or any non-React framework. React is an optional thin wrapper via createHook().
// Pure TypeScript / JavaScript — no React.
import { combineSlices, createSlice } from 'fluxo-ui/store';
const userSlice = createSlice('user', () => ({ name: 'Guest' }));
const flagsSlice = createSlice('flags', () => ({ darkMode: false }));
const store = combineSlices({ user: userSlice, flags: flagsSlice });
// Subscribe directly — these listeners fire in pure JS environments
// (Node, web workers, vanilla DOM) the same way they do in React.
store.on('change', 'user.name', (s) => {
console.log('user.name =>', s.user.name);
});
userSlice.on('change', (s) => {
console.log('user slice changed:', s);
});
// Writes from any side propagate everywhere.
userSlice.setState({ name: 'Alice' });
store.setState((s) => ({ flags: { ...s.flags, darkMode: true } }));
Import
import { createSlice, combineSlices, createHook, isSlice, isCombinedStore } from 'fluxo-ui/store';API Reference
createSlice(name, initializer, middlewares?)(name: string, initializer: () => T, middlewares?: Middleware<T>[]) => Slice<T>"-"Create a named slice. Usable directly as a Store<T> in standalone mode, or adopted by combineSlices() for composition
createSlice(name, initializer, middlewares?)(name: string, initializer: () => T, middlewares?: Middleware<T>[]) => Slice<T>"-"Create a named slice. Usable directly as a Store<T> in standalone mode, or adopted by combineSlices() for composition
combineSlices(slices, middlewares?)(slices: Record<string, Slice>, middlewares?) => CombinedStore"-"Compose multiple slices into a single store. Bidirectional sync: slice writes notify the combined store, combined writes notify the relevant slice's subscribers
combineSlices(slices, middlewares?)(slices: Record<string, Slice>, middlewares?) => CombinedStore"-"Compose multiple slices into a single store. Bidirectional sync: slice writes notify the combined store, combined writes notify the relevant slice's subscribers
slice.sliceNamestring"-"The name passed to createSlice. Used as the key in combined state
slice.sliceNamestring"-"The name passed to createSlice. Used as the key in combined state
slice.getState() / setState() / on() / reset()Same as Store<T>"-"Slices implement the full Store<T> interface — works identically standalone or after being combined
slice.getState() / setState() / on() / reset()Same as Store<T>"-"Slices implement the full Store<T> interface — works identically standalone or after being combined
combinedStore.slicesRecord<string, Slice>"-"The slices passed to combineSlices, exposed for direct slice access
combinedStore.slicesRecord<string, Slice>"-"The slices passed to combineSlices, exposed for direct slice access
isSlice(value) / isCombinedStore(value)(value: unknown) => boolean"-"Type guards for runtime checks
isSlice(value) / isCombinedStore(value)(value: unknown) => boolean"-"Type guards for runtime checks
Features
Standalone-Capable Slices
A slice is a full Store<T> on its own. Use it directly — combine later without changing call sites.
Bidirectional Sync
Writes via the slice or the combined store both fan out to slice subscribers and combined-store subscribers.
Single State Cell
No duplicated state. Slice and combined store share one cloned state per batch — zero double-write or fan-out cost.
Path Subscriptions Across Layers
Slice-local paths and combined fully-qualified paths both work — listeners only fire for their relevant changes.
Microtask-Cached Reads
Slice.getState() caches the local branch within a microtask — repeated reads are O(1) without re-cloning the parent.
Framework-Agnostic
createSlice and combineSlices are pure TypeScript. React integration is optional via createHook(slice) or createHook(combined).