API: createStore
The createStore function is the foundation of SoulState. It creates a store instance that holds your application state and provides methods to read and update it.
createStore
Creates a new store instance with the provided initial state.
Signature
function createStore<T extends object>(
initialState: T
): Store<T>;
initialState: The initial state object for your store.- Returns: A
Store<T>instance withget,set, andsubscribemethods.
Basic Usage
import { createStore } from 'soulstate';
// Create a store with initial state
export const counterStore = createStore({
count: 0,
user: null
});
// Define actions as separate functions
export const counterActions = {
increment: () => counterStore.set((state) => ({ count: state.count + 1 })),
setUser: (user: any) => counterStore.set({ user }),
reset: () => counterStore.set({ count: 0, user: null })
};
Store Interface
createStore returns a Store object with three core methods:
export interface Store<T> {
get: () => T;
set: (updater: StateUpdater<T>) => void;
subscribe: <S>(
selector: (state: T) => S,
listener: (selectedState: S, prevSelectedState: S) => void,
options?: { equalityFn?: (a: S, b: S) => boolean }
) => () => void;
}
type StateUpdater<T> = Partial<T> | ((state: T) => Partial<T> | T);
Store Methods
store.get()
Returns the current state of the store.
- Signature:
() => T - Behavior: Provides a direct, non-reactive snapshot of the state. Does not create a subscription.
import { counterStore } from './store';
function logCurrentState() {
// Get current state without subscribing
const state = counterStore.get();
console.log('Current count:', state.count);
console.log('Current user:', state.user);
}
// In React components, prefer useStore for reactive reads
function Counter() {
const count = useStore(counterStore, state => state.count);
// ...
}
store.set()
Updates the store's state. Supports both direct object updates and functional updates.
- Signature:
(updater: StateUpdater<T>) => void - Behavior: All updates are automatically batched using microtasks.
Object Update:
// Direct object update - merges with current state
counterStore.set({ count: 5 });
counterStore.set({ user: { name: 'John', age: 30 } });
// Multiple properties at once
counterStore.set({ count: 10, user: null });
Functional Update:
// Recommended for updates that depend on current state
counterStore.set((state) => ({ count: state.count + 1 }));
counterStore.set((state) => ({
user: { ...state.user, age: state.user.age + 1 }
}));
Performance Optimization:
// SoulState automatically optimizes updates:
// - Skips updates if values haven't changed
// - Batches multiple updates into single notification
// - Uses structural sharing to minimize object creation
const handleOptimizedUpdate = () => {
counterStore.set({ count: 5 }); // 🚫 No immediate re-render
counterStore.set({ count: 5 }); // 🚫 Skipped (same value)
counterStore.set({ count: 10 }); // 🚫 No immediate re-render
// ✅ Single re-render with final state (count: 10)
};
store.subscribe()
Low-level method to subscribe to state changes. Powers the useStore React hook.
- Signature:
<S>(selector, listener, options?) => () => void - Returns: Unsubscribe function
Parameters:
selector: (state: T) => S- Extracts the slice of state to watchlistener: (newValue: S, oldValue: S) => void- Called when selected state changesoptions.equalityFn?: (a: S, b: S) => boolean- Custom equality function (default:Object.is)
import { counterStore } from './store';
// Subscribe to count changes
const unsubscribe = counterStore.subscribe(
(state) => state.count,
(newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`);
}
);
// Subscribe with custom equality (shallow comparison)
import { shallow } from 'soulstate/utils';
const unsubscribeUser = counterStore.subscribe(
(state) => ({ count: state.count, user: state.user }),
(newData, oldData) => {
console.log('Count or user changed:', newData);
},
{ equalityFn: shallow }
);
// Trigger updates
counterStore.set({ count: 10 });
counterStore.set({ user: { name: 'Alice' } });
// Clean up subscriptions
unsubscribe();
unsubscribeUser();
React Usage
In React components, always prefer the useStore hook over direct subscribe calls. useStore handles component lifecycle, cleanup, and integrates properly with React's rendering system.
Store Creation Patterns
Basic Store
// Simple counter store
export const counterStore = createStore({ count: 0 });
export const counterActions = {
increment: () => counterStore.set(state => ({ count: state.count + 1 })),
decrement: () => counterStore.set(state => ({ count: state.count - 1 })),
reset: () => counterStore.set({ count: 0 })
};
Complex Store with Multiple Actions
// User management store
interface UserState {
user: { id: string; name: string; email: string } | null;
isLoggedIn: boolean;
preferences: { theme: string; language: string };
}
export const userStore = createStore<UserState>({
user: null,
isLoggedIn: false,
preferences: { theme: 'light', language: 'en' }
});
export const userActions = {
login: (userData: any) => userStore.set({
user: userData,
isLoggedIn: true
}),
logout: () => userStore.set({
user: null,
isLoggedIn: false
}),
updatePreferences: (updates: Partial<UserState['preferences']>) =>
userStore.set(state => ({
preferences: { ...state.preferences, ...updates }
})),
updateProfile: (updates: Partial<UserState['user']>) =>
userStore.set(state => ({
user: state.user ? { ...state.user, ...updates } : null
}))
};
Advanced: Standalone Usage
While typically used with React, SoulState stores can be used in any JavaScript environment:
// Node.js or non-React usage
const appState = createStore({ status: 'idle', data: null });
// Subscribe to changes
const unsubscribe = appState.subscribe(
state => state.status,
(newStatus, oldStatus) => {
console.log(`Status: ${oldStatus} → ${newStatus}`);
}
);
// Update state
appState.set({ status: 'loading' });
appState.set(state => ({ data: { items: [] }, status: 'success' }));
// Get current state
console.log('Current state:', appState.get());
// Clean up
unsubscribe();
Key Takeaway
createStore provides a minimal yet powerful foundation for state management. With just three methods—get, set, and subscribe—you get automatic batching, optimal performance, and seamless React integration.