react-state-custom 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ ## Project Snapshot
2
+ - `react-state-custom` is a hook-first state management library; entrypoint `src/index.ts` re-exports context factories and subscription hooks.
3
+ - TypeScript only; consumers are expected to be React 19 apps (see `package.json` peerDependencies).
4
+ - Library build artifacts live in `dist/`; all source utilities reside under `src/state-utils/`.
5
+
6
+ ## Context Core
7
+ - `Context` (`src/state-utils/ctx.ts`) extends `EventTarget`; `data` stores the latest values and `publish` fires per-key DOM events when a loose `!=` comparison detects change.
8
+ - `getContext` memoizes by context name; `useDataContext(name)` memoizes the lookup via React `useMemo`.
9
+ - `useDataSource`/`useDataSourceMultiple` publish new values and register keys in `ctx.registry`; duplicate sources log via `useRegistryChecker`.
10
+
11
+ ## Root Contexts
12
+ - `createRootCtx(name, useFn)` (`src/state-utils/createRootCtx.tsx`) wraps your hook (`useFn`) in a hidden `Root` component that pushes its result into a derived context namespace.
13
+ - Context names come from `name` plus sorted prop key/value pairs; keep props serializable and stable to avoid collisions.
14
+ - `ctxMountedCheck` blocks multiple `Root` instances for the same name; duplicates throw with the original call stack.
15
+ - Use `useCtxStateStrict` to hard-fail when the `Root` is missing, or `useCtxState` to surface a delayed console error instead.
16
+
17
+ ## Auto Context Lifecycle
18
+ - `AutoRootCtx` (`src/state-utils/createAutoCtx.tsx`) maintains a global `'auto-ctx'` context exposing `subscribe(Root, params)` and reference counts for each instance.
19
+ - Mount `<AutoRootCtx Wrapper={YourErrorBoundary}>` once near the app root; it renders requested `Root` components inside the optional wrapper.
20
+ - `createAutoCtx(rootCtx, unmountTime?)` returns `useCtxState(params)` that subscribes through `'auto-ctx'` and hands back `useDataContext` for the resolved name.
21
+ - Multiple callers with identical params share a single `Root`; `unmountTime` delays teardown to absorb rapid mount/unmount cycles.
22
+
23
+ ## Subscription Patterns
24
+ - `useDataSourceMultiple(ctx, ...entries)` expects stable `[key, value]` tuples; internal `useArrayHash` hashes length and reference equality only.
25
+ - Use `useDataSubscribe(ctx, key, debounceMs)` or `useDataSubscribeMultiple(ctx, ...keys)` for keyed reads; debounce variants batch updates when values bounce.
26
+ - `useDataSubscribeWithTransform` recomputes the transformed shape only on change; memoize the `transform` fn to avoid churn.
27
+ - `useQuickSubscribe(ctx)` returns a proxy over `ctx.data`; destructure needed fields immediately during render and avoid storing the proxy for later use.
28
+
29
+ ## Utilities and Gotchas
30
+ - Value comparisons are shallow; mutate objects before republishing or provide new references so `publish` sees changes.
31
+ - `useArrayHash`/`useObjectHash` generate random hashes when array/object shape changes; they do not deep-compare nested content.
32
+ - The reserved `'auto-ctx'` namespace powers `AutoRootCtx`; do not manually reuse this context name.
33
+
34
+ ## Build and Tooling
35
+ - `yarn build` runs Vite with `@vitejs/plugin-react`, `vite-plugin-dts`, and `vite-bundle-analyzer`; the analyzer spins up a server after builds—stop it in CI if unused.
36
+ - `yarn dev` starts the Vite dev server on port 3000; useful for manual inspection of bundle output.
37
+ - Tests are stubbed (`yarn test` exits 0 after printing "No tests specified"); add coverage before depending on test gates.
38
+ - Repo is Yarn 4 (PnP); if editors struggle with module resolution, run `./fix-vscode-yarn-pnp.sh`.
39
+
40
+ ## Reference Docs
41
+ - README (`README.md`) offers narrative examples and positioning.
42
+ - API reference (`API_DOCUMENTATION.md`) documents every export; update alongside code changes.
package/README.md CHANGED
@@ -1,142 +1,320 @@
1
- # React State Custom - Documentation
1
+ # React State Custom
2
2
 
3
- Welcome to the comprehensive documentation for the `react-state-custom` library!
3
+ **Simple. Powerful. TypeScript-first.**
4
4
 
5
- ## 📚 Documentation Files
6
-
7
- - **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference with examples for all exported functions, hooks, and classes
8
-
9
- ## 🚀 Quick Start
5
+ A lightweight React state management library that combines the simplicity of React hooks with the power of event-driven subscriptions. No boilerplate, no complexity—just pure, performant state management.
10
6
 
11
7
  ```bash
12
8
  npm install react-state-custom
13
9
  ```
14
10
 
15
- ## 📖 What's Inside
11
+ ## Why React State Custom?
12
+
13
+ **Zero Boilerplate** • **Type-Safe** • **Selective Re-renders** • **Hook-Based** • **~10KB Bundle**
14
+
15
+ React State Custom lets you write state management code that feels natural—because it **is** just React hooks. Use the same hooks you already know (`useState`, `useEffect`, etc.) to create powerful, shared state without learning new paradigms.
16
+
17
+ ## ⚡ Quick Example
18
+
19
+ ```typescript
20
+ import { createRootCtx, createAutoCtx, useQuickSubscribe, AutoRootCtx } from 'react-state-custom';
21
+
22
+ // 1. Write your state logic using familiar React hooks
23
+ function useCounterState() {
24
+ const [count, setCount] = useState(0);
25
+ const increment = () => setCount(c => c + 1);
26
+ const decrement = () => setCount(c => c - 1);
27
+
28
+ return { count, increment, decrement };
29
+ }
30
+
31
+ // 2. Create shared context (one line!)
32
+ const { useCtxState } = createAutoCtx(createRootCtx('counter', useCounterState));
33
+
34
+ // 3. Add AutoRootCtx to your app root
35
+ function App() {
36
+ return (
37
+ <>
38
+ <AutoRootCtx />
39
+ <Counter />
40
+ </>
41
+ );
42
+ }
43
+
44
+ // 4. Use anywhere in your app
45
+ function Counter() {
46
+ const ctx = useCtxState();
47
+ const { count, increment, decrement } = useQuickSubscribe(ctx);
48
+
49
+ return (
50
+ <div>
51
+ <h1>{count}</h1>
52
+ <button onClick={increment}>+</button>
53
+ <button onClick={decrement}>-</button>
54
+ </div>
55
+ );
56
+ }
57
+ ```
58
+
59
+ **That's it!** No reducers, no actions, no providers to wrap—just hooks.
60
+
61
+ ## 🎯 Key Features
62
+
63
+ ### 1. **Just React Hooks**
64
+ Use `useState`, `useEffect`, `useMemo`, and any other React hooks you already know. No new concepts to learn.
65
+
66
+ ```typescript
67
+ function useUserState({ userId }: { userId: string }) {
68
+ const [user, setUser] = useState(null);
69
+ const [loading, setLoading] = useState(true);
70
+
71
+ useEffect(() => {
72
+ fetchUser(userId).then(setUser).finally(() => setLoading(false));
73
+ }, [userId]);
74
+
75
+ return { user, loading };
76
+ }
77
+ ```
16
78
 
17
- The `react-state-custom` library provides a powerful set of tools for managing shared state in React applications:
79
+ ### 2. **Selective Re-renders**
80
+ Components only re-render when the **specific data they subscribe to** changes—not when anything in the state changes.
18
81
 
19
- ### Core Features
82
+ ```typescript
83
+ // Only re-renders when 'user' changes, not when 'loading' changes
84
+ const { user } = useDataSubscribeMultiple(ctx, 'user');
20
85
 
21
- - **Context System** - Type-safe context management with event-driven subscriptions
22
- - **Root Context Factory** - Automated context lifecycle management
23
- - **Auto Context System** - Self-managing context instances
24
- - **Utility Hooks** - Performance optimization tools
86
+ // Or subscribe to multiple fields
87
+ const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
88
+ ```
25
89
 
26
- ### Key Benefits
90
+ ### 3. **Automatic Context Management**
91
+ With `AutoRootCtx`, state contexts are automatically created and destroyed as needed. No manual provider management.
27
92
 
28
- - **Type Safety** - Full TypeScript support with strong typing
29
- - **Performance** - Only re-renders when subscribed data changes
30
- - ✅ **Flexibility** - Works with any data structure
31
- - ✅ **Developer Experience** - Rich debugging and error checking
32
- - ✅ **Minimal Boilerplate** - Automated context management
93
+ ### 4. **TypeScript First**
94
+ Full type inference and type safety throughout. Your IDE knows exactly what's in your state.
33
95
 
34
- ## 📝 Documentation Structure
96
+ ### 5. **Tiny Bundle Size**
97
+ ~10KB gzipped. No dependencies except React.
35
98
 
36
- The [API Documentation](./API_DOCUMENTATION.md) is organized into the following sections:
99
+ ## 🆚 Comparison with Redux & Zustand
37
100
 
38
- 1. **Core Context System** - Basic context functionality
39
- 2. **Data Source Hooks** - Publishing data to contexts
40
- 3. **Data Subscription Hooks** - Subscribing to context changes
41
- 4. **Root Context Factory** - Advanced context patterns
42
- 5. **Auto Context System** - Automated context management
43
- 6. **Utility Hooks** - Performance and utility functions
44
- 7. **Usage Patterns** - Common implementation patterns
45
- 8. **Examples** - Complete application examples
101
+ | Feature | React State Custom | Redux | Zustand |
102
+ |---------|-------------------|-------|---------|
103
+ | **Bundle Size** | ~10KB | ~50KB (with toolkit) | ~1KB |
104
+ | **Learning Curve** | Minimal (just hooks) | ❌ High (actions, reducers, middleware) | ✅ Low |
105
+ | **Boilerplate** | None | Heavy | ✅ Minimal |
106
+ | **Type Safety** | Full inference | ⚠️ Requires setup | ✅ Good |
107
+ | **Selective Re-renders** | ✅ Built-in | ⚠️ Requires selectors | ✅ Built-in |
108
+ | **DevTools** | ⚠️ Console logging | ✅ Redux DevTools | ✅ DevTools support |
109
+ | **Async Support** | ✅ Native (hooks) | ⚠️ Requires middleware | ✅ Native |
110
+ | **Context Composition** | ✅ Automatic | ❌ Manual | ⚠️ Manual store combination |
46
111
 
47
- ## 🎯 Common Use Cases
112
+ ### When to Use React State Custom
48
113
 
49
- - **Global State Management** - Application-wide state without Redux complexity
50
- - **Component Communication** - Share data between distant components
51
- - **Performance Optimization** - Minimize unnecessary re-renders
52
- - **Context Composition** - Combine multiple contexts efficiently
114
+ **Choose React State Custom if you:**
115
+ - Want to use React hooks for state management without learning new patterns
116
+ - Need fine-grained control over component re-renders
117
+ - Prefer minimal boilerplate and configuration
118
+ - Want automatic context lifecycle management
119
+ - Need multiple independent state contexts that don't interfere
53
120
 
54
- ## 🔧 Quick Example
121
+ **Consider Redux if you:**
122
+ - Need powerful time-travel debugging (Redux DevTools)
123
+ - Have a very large team that benefits from strict architectural patterns
124
+ - Already have significant Redux investment
55
125
 
56
- ### Using createRootCtx for Advanced State Management
126
+ **Consider Zustand if you:**
127
+ - Want the absolute smallest bundle size
128
+ - Need a simple global store without context isolation
129
+ - Don't need automatic context lifecycle management
57
130
 
131
+ ## 🔥 Real-World Example: User Authentication
58
132
 
59
- file main.tsx
60
133
  ```typescript
61
- import { AutoRootCtx } from 'react-state-custom';
134
+ // authState.ts
135
+ function useAuthState() {
136
+ const [user, setUser] = useState<User | null>(null);
137
+ const [loading, setLoading] = useState(true);
138
+
139
+ useEffect(() => {
140
+ // Check authentication on mount
141
+ checkAuth().then(setUser).finally(() => setLoading(false));
142
+ }, []);
143
+
144
+ const login = async (email: string, password: string) => {
145
+ setLoading(true);
146
+ try {
147
+ const user = await authService.login(email, password);
148
+ setUser(user);
149
+ } finally {
150
+ setLoading(false);
151
+ }
152
+ };
153
+
154
+ const logout = async () => {
155
+ await authService.logout();
156
+ setUser(null);
157
+ };
158
+
159
+ return { user, loading, login, logout };
160
+ }
62
161
 
63
- // Root Component
64
- function App({children}) {
162
+ export const { useCtxState: useAuthState } = createAutoCtx(
163
+ createRootCtx('auth', useAuthState)
164
+ );
165
+
166
+ // App.tsx
167
+ function App() {
65
168
  return (
66
169
  <>
67
170
  <AutoRootCtx />
68
- {children}
171
+ <Router>
172
+ <Header />
173
+ <Routes />
174
+ </Router>
69
175
  </>
70
176
  );
71
177
  }
72
178
 
73
- ```
74
- file userState.ts
75
- ```typescript
76
- import { createRootCtx, createAutoCtx, } from 'react-state-custom';
77
-
78
- interface UserState {
79
- user: User | null;
80
- loading: boolean;
81
- error: string | null;
179
+ // Header.tsx - Only re-renders when user changes
180
+ function Header() {
181
+ const ctx = useAuthState();
182
+ const { user, logout } = useQuickSubscribe(ctx);
183
+
184
+ return (
185
+ <header>
186
+ {user ? (
187
+ <>
188
+ <span>Welcome, {user.name}</span>
189
+ <button onClick={logout}>Logout</button>
190
+ </>
191
+ ) : (
192
+ <Link to="/login">Login</Link>
193
+ )}
194
+ </header>
195
+ );
82
196
  }
83
197
 
84
- // Create a state hook
85
- function useUserState(props: { userId: string }) {
86
- const [user, setUser] = useState<User | null>(null);
87
- const [loading, setLoading] = useState(true);
88
- const [error, setError] = useState<string | null>(null);
198
+ // ProtectedRoute.tsx - Only re-renders when loading or user changes
199
+ function ProtectedRoute({ children }) {
200
+ const ctx = useAuthState();
201
+ const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
89
202
 
90
- useEffect(() => {
91
- fetchUser(props.userId).then(setUser).catch(setError).finally(() => setLoading(false));
92
- }, [props.userId]);
203
+ if (loading) return <Spinner />;
204
+ if (!user) return <Navigate to="/login" />;
93
205
 
94
- return { user, loading, error };
206
+ return children;
95
207
  }
208
+ ```
209
+
210
+ **Compare with Redux:**
211
+ ```typescript
212
+ // Redux requires: action types, action creators, reducers, thunks/sagas
213
+ // React State Custom: just write a hook! ✨
214
+ ```
215
+
216
+ ## 📚 Core Concepts
96
217
 
97
- // Register State hook
98
- const { useCtxState: useUserCtxState } = createAutoCtx(createRootCtx(
99
- 'user-state',
100
- useUserState
101
- ));
218
+ ### 1. **Context** - Event-driven state container
219
+ ```typescript
220
+ const ctx = useDataContext<MyState>('my-state');
221
+ ```
222
+
223
+ ### 2. **Data Source** - Publish values to context
224
+ ```typescript
225
+ useDataSource(ctx, 'count', count);
226
+ ```
102
227
 
103
- export { useUserCtxState }
228
+ ### 3. **Data Subscription** - Subscribe to specific values
229
+ ```typescript
230
+ const count = useDataSubscribe(ctx, 'count');
231
+ const { count, name } = useDataSubscribeMultiple(ctx, 'count', 'name');
232
+ ```
104
233
 
234
+ ### 4. **Root Context** - Lifecycle-managed context
235
+ ```typescript
236
+ const { Root, useCtxState } = createRootCtx('my-state', useMyState);
105
237
  ```
106
238
 
107
- file UserProfile.tsx
239
+ ### 5. **Auto Context** - Automatic instance management
108
240
  ```typescript
109
- import { useDataSubscribeMultiple, useQuickSubscribe } from 'react-state-custom';
110
- import { useUserCtxState } from "./userState.ts"
241
+ const { useCtxState } = createAutoCtx(rootContext);
242
+ ```
111
243
 
112
- // Consumer component using useDataSubscribeMultiple
113
- function UserProfile({ userId }: { userId: string }) {
114
- const ctx = useUserCtxState({ userId });
115
- const { user, loading, error } = useDataSubscribeMultiple(ctx, 'user', 'loading', 'error');
116
-
117
- if (loading) return <div>Loading user...</div>;
118
- if (error) return <div>Error: {error}</div>;
119
-
120
- return <div>Welcome, {user?.name}!</div>;
244
+ ## 🚀 Advanced Features
245
+
246
+ ### Parameterized Contexts
247
+ Create multiple instances of the same state with different parameters:
248
+
249
+ ```typescript
250
+ function useUserState({ userId }: { userId: string }) {
251
+ // State logic here
121
252
  }
122
253
 
123
- // Or using useQuickSubscribe for Simplified Access
254
+ const { useCtxState: useUserState } = createAutoCtx(
255
+ createRootCtx('user', useUserState)
256
+ );
124
257
 
125
- function UserProfileV2({ userId }: { userId: string }) {
126
- const ctx = useUserCtxState({ userId });
127
- const { user, loading, error } = useQuickSubscribe(ctx);
128
-
129
- if (loading) return <div>Loading user...</div>;
130
- if (error) return <div>Error: {error}</div>;
131
-
132
- return <div>Welcome, {user?.name}!</div>;
258
+ // Different instances for different users
259
+ function UserProfile({ userId }) {
260
+ const ctx = useUserState({ userId }); // Automatic instance per userId
261
+ const { user } = useQuickSubscribe(ctx);
262
+ return <div>{user?.name}</div>;
133
263
  }
134
264
  ```
135
265
 
266
+ ### Debounced Subscriptions
267
+ Optimize performance for frequently changing values:
268
+
269
+ ```typescript
270
+ // Re-render at most once per 300ms
271
+ const searchQuery = useDataSubscribe(ctx, 'searchQuery', 300);
272
+ ```
273
+
274
+ ### Transformed Subscriptions
275
+ Transform data before using it:
276
+
277
+ ```typescript
278
+ const userStats = useDataSubscribeWithTransform(
279
+ ctx,
280
+ 'user',
281
+ (user) => ({
282
+ fullName: `${user?.firstName} ${user?.lastName}`,
283
+ isAdmin: user?.role === 'admin'
284
+ })
285
+ );
286
+ ```
287
+
288
+ ## 📖 Documentation
289
+
290
+ For complete API documentation, examples, and advanced patterns, see:
291
+ - **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference
292
+
293
+ ## 🎓 Learning Path
294
+
295
+ 1. **Start Simple** - Use `createRootCtx` + `createAutoCtx` for basic state
296
+ 2. **Add Subscriptions** - Use `useDataSubscribeMultiple` for selective re-renders
297
+ 3. **Optimize** - Add debouncing and transformations as needed
298
+ 4. **Scale** - Create parameterized contexts for dynamic instances
299
+
300
+ ## 📦 Installation
301
+
302
+ ```bash
303
+ npm install react-state-custom
304
+ # or
305
+ yarn add react-state-custom
306
+ # or
307
+ pnpm add react-state-custom
308
+ ```
309
+
310
+ ## 🤝 Contributing
311
+
312
+ Contributions are welcome! Please feel free to submit a Pull Request.
313
+
136
314
  ## 📄 License
137
315
 
138
- MIT License - see the main repository for details.
316
+ MIT License - feel free to use in any project.
139
317
 
140
318
  ---
141
319
 
142
- For the complete API reference with detailed examples, see [API_DOCUMENTATION.md](./API_DOCUMENTATION.md).
320
+ **Made with ❤️ for developers who love React hooks**
@@ -0,0 +1,4 @@
1
+ export declare const DevToolContainer: ({ toggleButton, ...props }: {
2
+ [x: string]: any;
3
+ toggleButton?: string | undefined;
4
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ export declare const DevToolState: ({}: {}) => import("react/jsx-runtime").JSX.Element;
3
+ export declare const StateView: React.FC<{
4
+ dataKey: string;
5
+ }>;
6
+ export declare const JSONView: React.FC<{
7
+ value: any;
8
+ name?: string;
9
+ style?: any;
10
+ expandLevel?: number | boolean;
11
+ }>;
package/dist/Test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const Test: ({}: {}) => import("react/jsx-runtime").JSX.Element;
package/dist/dev.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export { createRootCtx } from './state-utils/createRootCtx';
3
3
  export { AutoRootCtx, createAutoCtx } from './state-utils/createAutoCtx';
4
4
  export { useArrayHash } from './state-utils/useArrayHash';
5
5
  export { useQuickSubscribe } from './state-utils/useQuickSubscribe';
6
+ export { DevToolContainer } from './DevTool';