Subtitle:
Centralized State Management for React Components Using Custom Store Factories
Core Idea:
The React Store Pattern creates a centralized state container with subscription capabilities that serves as a single source of truth for component trees, allowing components to access and update shared state without prop drilling or context overhead.
Key Principles:
-
Centralized State Management:
- Maintains all relevant state in a single store object
- Provides controlled access to read and update state
-
Subscription Model:
- Components subscribe to specific state changes
- Updates trigger selective component re-renders
-
Encapsulated API:
- Exposes limited methods for state interaction
- Abstracts implementation details from consumers
Why It Matters:
-
Reduced Component Coupling:
- Components interact with the store, not directly with each other
- Makes code more maintainable and testable
-
Optimized Rendering:
- Only components that subscribe to changed state re-render
- Avoids the all-or-nothing updates of Context API
-
Predictable State Flow:
- Creates clear data flow paths through the application
- Makes debugging and reasoning about state changes easier
How to Implement:
-
Create Store Factory:
- Implement a function that generates store instances
- Use closures to maintain private state
-
Subscription System:
- Implement subscribe/unsubscribe mechanisms
- Track listeners for each state slice
-
Component Integration:
- Hook into component lifecycle with useEffect
- Clean up subscriptions when components unmount
Example:
import React, { useEffect, useState } from 'react';
// Generic createStore function
function createStore<T>(initialState: T) {
// Private state
let state = { ...initialState };
const listeners: Record<string, Array<(value: any) => void>> = {};
// Subscribe to changes for a specific key
const subscribe = <K extends keyof T>(
key: K,
callback: (value: T[K]) => void
) => {
if (!listeners[key as string]) {
listeners[key as string] = [];
}
listeners[key as string].push(callback);
// Return unsubscribe function
return () => {
listeners[key as string] = listeners[key as string].filter(
listener => listener !== callback
);
};
};
// Update a specific key in the store
const update = <K extends keyof T>(key: K, value: T[K]) => {
state[key] = value;
// Notify all listeners for this key
if (listeners[key as string]) {
listeners[key as string].forEach(listener => listener(value));
}
};
// Get current value for a key
const get = <K extends keyof T>(key: K): T[K] => state[key];
return { subscribe, update, get };
}
// Example usage with a component
function StoreConsumer({ store }) {
const [count, setCount] = useState(store.get('count'));
useEffect(() => {
// Subscribe to count changes
const unsubscribe = store.subscribe('count', setCount);
// Clean up subscription when component unmounts
return unsubscribe;
}, [store]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => store.update('count', count + 1)}>
Increment
</button>
</div>
);
}
Connections:
-
State Management Approaches:
- Redux: More formalized implementation of similar patterns
- Context API: React's built-in state sharing solution
- Zustand: Modern implementation of store pattern
-
React Concepts:
- useMemo: Often used to create stable store instances
- Custom Hooks: Common way to encapsulate store access
- React Component Communication: Patterns for inter-component data flow
-
Implementation Examples:
- Coordinate Marker Plugin: Uses store pattern for state management
- React PDF Viewer createStore Function: Specific implementation in React PDF Viewer
References:
-
Primary Source:
- React Advanced Patterns documentation
-
Additional Resources:
- "State Management with React Hooks" by Kent C. Dodds
- "Build Your Own React State Management" by Tanner Linsley
Tags:
#react #state-management #store #subscription #patterns #custom-hooks
Connections:
Sources:
- From: Coordinate Marker Plugin