Skip to main content

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 with get, set, and subscribe methods.

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 watch
  • listener: (newValue: S, oldValue: S) => void - Called when selected state changes
  • options.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.