Modular Store Design (Slices)
As applications grow, a single createStore call with hundreds of lines can become unmanageable. SoulState's Slice Abstraction allows you to break your state into focused, independent modules while maintaining a unified, systems-grade runtime.
Defining Slices
A slice encapsulates a specific domain of your application state and the logic that operates on it.
// features/auth/authSlice.ts
import { createSlice } from 'soulstate';
export const authSlice = createSlice({
name: 'auth',
initialState: { user: null, token: null },
reducers: {
login: (state, user) => ({ user }),
logout: () => ({ user: null, token: null })
}
});
Composing the Root Store
Slices are combined using combineSlices and then passed to createStore. This creates a single root state object where each slice's name is a top-level key.
// store.ts
import { createStore, combineSlices } from 'soulstate';
import { authSlice } from './features/auth/authSlice';
import { themeSlice } from './features/theme/themeSlice';
export const store = createStore(
combineSlices(authSlice, themeSlice)
);
// Final state structure:
// {
// auth: { user: null, token: null, login: f, logout: f },
// theme: { mode: 'dark', setTheme: f }
// }
Accessing Modular State
In components, you use the slice name to drill into the specific domain. Because of SoulState's Surgical Tracking, subscribing to s => s.auth.user will not cause re-renders if theme.mode changes.
function Profile() {
const user = useStore(store, s => s.auth.user);
return <div>{user.name}</div>;
}
Modular Actions
Actions in a slice are automatically injected into the state tree. This keeps your API discoverable and grouped by domain.
function LogoutButton() {
// Actions are stable and part of the slice state
const { logout } = useStore(store, s => s.auth);
return <button onClick={logout}>Log Out</button>;
}
Scaling Strategies
- Slice per Feature: Create a slice for every major feature (Auth, Settings, DataGrid, etc.).
- Shared Slices: Use a
sharedslice for cross-cutting data like notifications or configuration. - Cross-Slice Logic: If an action needs to update multiple slices, define it as a standard action that calls
store.setStateat the root level, rather than inside a specific slice's reducers.
No Performance Penalty
Modularizing your store with slices has no runtime performance cost. SoulState's propagation engine treats the combined tree as a single graph, providing the same systems-grade throughput as a flat store.