Store Pattern API
The Store Pattern provides type-safe state management through the createDeclarativeStorePattern
function, offering excellent type inference and simplified API focused on reactive state management.
Import
import { createDeclarativeStorePattern, useStoreValue } from '@context-action/react';
createDeclarativeStorePattern
createDeclarativeStorePattern<T>(name, config)
Creates a complete Store Pattern with Provider, hooks, and store management capabilities.
Type Parameters:
T
- Store configuration object with store definitions
Parameters:
name: string
- Pattern name for debugging and identificationconfig: T
- Store configuration object
Returns: StorePatternResult<T>
interface StorePatternResult<T> {
Provider: React.ComponentType<{ children: React.ReactNode }>;
useStore: <K extends keyof T>(key: K) => Store<InferStoreValue<T[K]>>;
useStoreManager: () => StoreManager<T>;
withProvider: (Component: React.ComponentType) => React.ComponentType;
}
Store Configuration
Direct Value Configuration
const {
Provider: UserStoreProvider,
useStore: useUserStore,
useStoreManager: useUserStoreManager,
withProvider: withUserStoreProvider
} = createDeclarativeStorePattern('User', {
// Direct value - type inferred automatically
profile: { name: '', email: '', avatar: '' },
preferences: { theme: 'light', language: 'en' },
settings: { notifications: true, privacy: 'public' }
});
Configuration Object Support
const {
Provider: AppStoreProvider,
useStore: useAppStore,
useStoreManager: useAppStoreManager
} = createDeclarativeStorePattern('App', {
// Configuration with initialValue
user: {
initialValue: { id: '', name: '', email: '', isAuthenticated: false }
},
// Configuration with validator
preferences: {
initialValue: { theme: 'light', language: 'en' },
validator: (value) =>
typeof value === 'object' &&
'theme' in value &&
'language' in value
},
// Configuration with derived state
analytics: {
initialValue: { events: [], sessionId: '' },
derived: {
eventCount: (analytics) => analytics.events.length,
hasEvents: (analytics) => analytics.events.length > 0
}
}
});
Provider Component
<Provider>
Context provider that manages store instances and provides store capabilities to child components.
Props:
children: React.ReactNode
- Child components
function App() {
return (
<UserStoreProvider>
<UserProfile />
<UserSettings />
</UserStoreProvider>
);
}
withProvider(Component)
Higher-Order Component that automatically wraps a component with the store provider.
Parameters:
Component: React.ComponentType
- Component to wrap
Returns: React.ComponentType
- Wrapped component
// Manual provider wrapping
function App() {
return (
<UserStoreProvider>
<UserComponent />
</UserStoreProvider>
);
}
// HOC pattern (recommended)
const App = withUserStoreProvider(() => (
<UserComponent />
));
Store Access Hook
useStore(key)
Access a specific store instance from the pattern.
Parameters:
key: keyof T
- Store key from configuration
Returns: Store<StoreValue>
- Store instance
function UserComponent() {
const profileStore = useUserStore('profile');
const preferencesStore = useUserStore('preferences');
// Store instances provide getValue, setValue, update methods
const profile = profileStore.getValue();
return <div>{profile.name}</div>;
}
Store Instance Methods
getValue()
Get current store value (non-reactive).
Returns: StoreValue
- Current value
const profileStore = useUserStore('profile');
const currentProfile = profileStore.getValue();
// { name: '', email: '', avatar: '' }
setValue(value)
Set new store value completely.
Parameters:
value: StoreValue
- New value
Returns: void
profileStore.setValue({
name: 'John Doe',
email: 'john@example.com',
avatar: 'https://example.com/avatar.jpg'
});
update(updater)
Update store value using updater function.
Parameters:
updater: (current: StoreValue) => StoreValue
- Update function
Returns: void
// Partial update
profileStore.update(current => ({
...current,
name: 'John Doe Updated'
}));
// Conditional update
preferencesStore.update(current => ({
...current,
theme: current.theme === 'light' ? 'dark' : 'light'
}));
Reactive Subscriptions
useStoreValue(store)
Subscribe to store changes and get reactive updates.
Parameters:
store: Store<T>
- Store instance fromuseStore()
Returns: T
- Current store value (reactive)
function UserProfile() {
const profileStore = useUserStore('profile');
const preferencesStore = useUserStore('preferences');
// Reactive subscriptions - components re-render on changes
const profile = useStoreValue(profileStore);
const preferences = useStoreValue(preferencesStore);
const updateProfile = () => {
profileStore.setValue({
...profile,
name: 'Updated Name'
});
// Component automatically re-renders with new name
};
return (
<div>
<h1>{profile.name}</h1>
<p>Theme: {preferences.theme}</p>
<button onClick={updateProfile}>Update</button>
</div>
);
}
Store Manager
useStoreManager()
Access the store manager for pattern-wide operations.
Returns: StoreManager<T>
- Store manager instance
interface StoreManager<T> {
reset(key: keyof T): void;
resetAll(): void;
getAllValues(): { [K in keyof T]: InferStoreValue<T[K]> };
getStore<K extends keyof T>(key: K): Store<InferStoreValue<T[K]>>;
}
Store Manager Methods
reset(key)
Reset a specific store to its initial value.
Parameters:
key: keyof T
- Store key to reset
function ResetComponent() {
const storeManager = useUserStoreManager();
const resetProfile = () => {
storeManager.reset('profile');
// profile store returns to: { name: '', email: '', avatar: '' }
};
return <button onClick={resetProfile}>Reset Profile</button>;
}
resetAll()
Reset all stores to their initial values.
const resetAllStores = () => {
storeManager.resetAll();
// All stores return to initial values
};
getAllValues()
Get current values from all stores (non-reactive).
Returns: Object with all current store values
const storeManager = useUserStoreManager();
const logAllValues = () => {
const values = storeManager.getAllValues();
console.log(values);
// {
// profile: { name: 'John', email: 'john@example.com', avatar: '' },
// preferences: { theme: 'dark', language: 'en' },
// settings: { notifications: true, privacy: 'private' }
// }
};
getStore(key)
Get store instance directly from manager.
Parameters:
key: keyof T
- Store key
Returns: Store<StoreValue>
- Store instance
function UtilityComponent() {
const storeManager = useUserStoreManager();
const updateProfileFromManager = () => {
const profileStore = storeManager.getStore('profile');
profileStore.setValue({ name: 'Manager Update', email: '', avatar: '' });
};
return <button onClick={updateProfileFromManager}>Update via Manager</button>;
}
Advanced Patterns
Derived State
const { useStore } = createDeclarativeStorePattern('Analytics', {
events: {
initialValue: [] as Array<{ type: string; timestamp: number }>,
derived: {
// Computed properties updated automatically
eventCount: (events) => events.length,
recentEvents: (events) => events.filter(e =>
Date.now() - e.timestamp < 300000 // Last 5 minutes
),
hasRecentActivity: (events) =>
events.some(e => Date.now() - e.timestamp < 60000) // Last minute
}
}
});
function AnalyticsComponent() {
const eventsStore = useAnalyticsStore('events');
const events = useStoreValue(eventsStore);
// Access derived state
console.log(events.eventCount); // Computed count
console.log(events.recentEvents); // Filtered events
console.log(events.hasRecentActivity); // Boolean flag
}
Store Validation
const { useStore } = createDeclarativeStorePattern('Settings', {
config: {
initialValue: { apiUrl: '', timeout: 5000, retries: 3 },
validator: (value) => {
if (typeof value !== 'object') return false;
if (!value.apiUrl || typeof value.apiUrl !== 'string') return false;
if (typeof value.timeout !== 'number' || value.timeout < 1000) return false;
if (typeof value.retries !== 'number' || value.retries < 0) return false;
return true;
}
}
});
function ConfigComponent() {
const configStore = useSettingsStore('config');
const updateConfig = () => {
try {
configStore.setValue({
apiUrl: 'https://api.example.com',
timeout: 3000,
retries: 5
}); // ✅ Passes validation
} catch (error) {
console.error('Invalid config:', error);
}
};
}
Multi-Store Coordination
function UserDashboard() {
const userStore = useUserStore('profile');
const preferencesStore = useUserStore('preferences');
const settingsStore = useUserStore('settings');
const user = useStoreValue(userStore);
const preferences = useStoreValue(preferencesStore);
const settings = useStoreValue(settingsStore);
const updateUserTheme = (newTheme: 'light' | 'dark') => {
// Update preferences
preferencesStore.update(current => ({
...current,
theme: newTheme
}));
// Update settings based on theme
settingsStore.update(current => ({
...current,
contrast: newTheme === 'dark' ? 'high' : 'normal'
}));
};
return (
<div className={`theme-${preferences.theme}`}>
<h1>Welcome, {user.name}</h1>
<button onClick={() => updateUserTheme('dark')}>
Switch to Dark Mode
</button>
</div>
);
}
Best Practices
When to Use Store Pattern
✅ Ideal for:
- Application state management
- Form state and UI state
- Data caching and persistence
- Derived state and computed values
❌ Avoid for:
- Complex business logic (use Action Pattern)
- Side effects and API calls (use Action Pattern)
- Event handling and analytics (use Action Pattern)
Performance Optimization
// Use React.memo for expensive components
const ExpensiveComponent = React.memo(function ExpensiveComponent() {
const dataStore = useAppStore('largeDataset');
const data = useStoreValue(dataStore);
// Expensive computation
const processedData = useMemo(() => {
return expensiveProcessing(data);
}, [data]);
return <div>{processedData}</div>;
});
// Selective subscriptions for large objects
function OptimizedComponent() {
const userStore = useAppStore('user');
const user = useStoreValue(userStore);
// Only re-render when name changes
const userName = useMemo(() => user.name, [user.name]);
return <h1>{userName}</h1>;
}
Examples
See the Store Only Pattern Example for complete working examples.
Related
- Store Manager API - Store management utilities
- Action Context API - Action Only pattern
- Main Patterns Guide - Pattern usage guide
- Basic Setup Example - Complete integration example