react-state-custom 1.0.32 → 1.0.33

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.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # React State Custom
2
2
 
3
- **Simple. Powerful. TypeScript-first.**
3
+ **The "It's Just a Hook" State Manager for React.**
4
4
 
5
- A lightweight global 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.
5
+ Turn any React hook into a global store. Zero boilerplate. Full type safety. Automatic lifecycle management.
6
6
 
7
7
  [![Demo](https://img.shields.io/badge/Demo-Live-blue?style=flat-square)](https://vothanhdat.github.io/react-state-custom/)
8
8
  [![npm version](https://img.shields.io/npm/v/react-state-custom?style=flat-square)](https://www.npmjs.com/package/react-state-custom)
@@ -14,517 +14,178 @@ npm install react-state-custom
14
14
 
15
15
  🎮 **[Try the Live Demo →](https://vothanhdat.github.io/react-state-custom/)**
16
16
 
17
- ## Quick Start (2 minutes)
17
+ ---
18
18
 
19
- If you already know how to write a component with `useState`, you're moments away from sharing that state everywhere.
19
+ ## The 30-Second Pitch
20
20
 
21
- 1. **Write a plain hook** encapuslate data fetching, derived values, and actions inside a normal React hook.
22
- 2. **Create a store** – `createStore('feature', useFeatureState)` creates a shared store and returns a `useStore` hook.
23
- 3. **Mount `<AutoRootCtx />` once** – drop it near the top of your tree (wrap it with your own `ErrorBoundary` if desired).
24
- 4. **Consume anywhere** – call the generated `useStore` hook to access data and actions.
21
+ Stop writing reducers, actions, and manual providers. If you can write a React hook, you've already written your store.
25
22
 
26
23
  ```tsx
27
- const useFeatureState = ({ featureId }: { featureId: string }) => {
28
- const [value, setValue] = useState(0)
29
- const double = useMemo(() => value * 2, [value])
30
- return { value, double, increment: () => setValue(v => v + 1) }
31
- }
32
-
33
- export const { useStore: useFeatureStore } = createStore('feature', useFeatureState)
34
-
35
- function AppShell() {
36
- return (
37
- <>
38
- <AutoRootCtx Wrapper={ErrorBoundary} debugging={import.meta.env.DEV} />
39
- <Routes />
40
- </>
41
- )
42
- }
43
-
44
- function FeatureMeter({ featureId }: { featureId: string }) {
45
- const { value, double, increment } = useFeatureStore({ featureId })
46
- return (
47
- <section>
48
- <strong>{value}</strong>
49
- <em>{double}</em>
50
- <button onClick={increment}>Add</button>
51
- </section>
52
- )
53
- }
54
- ```
55
-
56
- That’s the entire workflow—no reducers, actions, or provider trees.
57
-
58
- ## Why React State Custom?
59
-
60
- **Zero Boilerplate** • **Type-Safe** • **Selective Re-renders** • **Hook-Based** • **~10KB Bundle**
61
-
62
- 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 global state without learning new paradigms.
63
-
64
- ### When `useState` + `useEffect` Fall Short
65
-
66
- Even though React hooks are flexible, they start to hurt once state crosses component boundaries:
67
-
68
- - **Prop drilling & manual providers** – every time state needs to be shared, you create a context, memoize values, and remember to wrap trees.
69
- - **Coarse-grained re-renders** – updating one property forces every subscriber of that context to render, even if they don't consume the changed field.
70
- - **Lifecycle bookkeeping** – you manually manage instance lifetimes, clean up effects, and guard against components mounting before providers.
71
- - **Zero visibility** – there's no built-in way to inspect shared state, throttle noisy updates, or keep debugging breadcrumbs.
72
-
73
- React State Custom keeps your favorite hooks but layers on automatic context lifecycles, selective subscriptions, and built-in tooling so you can stay productive as your app grows.
74
-
75
- ## ⚡ Quick Example
76
-
77
- ### Without React State Custom (manual context plumbing)
78
-
79
- ```typescript
80
- const CounterContext = createContext<{
81
- count: number;
82
- increment: () => void;
83
- decrement: () => void;
84
- } | null>(null);
85
-
86
- function CounterProvider({ children }: { children: React.ReactNode }) {
87
- const [count, setCount] = useState(0);
88
- const value = useMemo(
89
- () => ({
90
- count,
91
- increment: () => setCount(c => c + 1),
92
- decrement: () => setCount(c => c - 1),
93
- }),
94
- [count]
95
- );
96
-
97
- return <CounterContext.Provider value={value}>{children}</CounterContext.Provider>;
24
+ // 1. Write a standard hook (your store logic)
25
+ const useCountState = ({ initial = 0 }) => {
26
+ const [count, setCount] = useState(initial)
27
+ const increment = () => setCount(c => c + 1)
28
+ return { count, increment }
98
29
  }
99
30
 
100
- function useCounter() {
101
- const ctx = useContext(CounterContext);
102
- if (!ctx) throw new Error('CounterProvider missing');
103
- return ctx;
104
- }
105
- ```
106
-
107
- Every consumer re-renders whenever anything in `value` changes, you have to remember to wrap parts of the tree with `CounterProvider`, and tearing this pattern down for parameterized instances gets messy fast.
31
+ // 2. Create a store
32
+ export const { useStore } = createStore('counter', useCountState)
108
33
 
109
- ### With React State Custom (hook-first store)
110
-
111
- ### With React State Custom (hook-first store)
112
-
113
- ```typescript
114
- import { createStore, AutoRootCtx } from 'react-state-custom';
115
-
116
- // 1. Write your state logic using familiar React hooks
117
- function useCounterState() {
118
- const [count, setCount] = useState(0);
119
- const increment = () => setCount(c => c + 1);
120
- const decrement = () => setCount(c => c - 1);
121
-
122
- return { count, increment, decrement };
123
- }
124
-
125
- // 2. Create shared store (one line!)
126
- const { useStore } = createStore('counter', useCounterState);
127
-
128
- // 3. Add AutoRootCtx to your app root (mount it once near the top of your tree)
34
+ // 3. Setup (mount once at root) & Use anywhere
129
35
  function App() {
130
36
  return (
131
37
  <>
132
- <AutoRootCtx />
38
+ <AutoRootCtx /> {/* 👈 The magic that manages your stores */}
133
39
  <Counter />
134
40
  </>
135
- );
41
+ )
136
42
  }
137
43
 
138
- // 4. Use anywhere in your app
139
44
  function Counter() {
140
- const { count, increment, decrement } = useStore({});
141
-
142
- return (
143
- <div>
144
- <h1>{count}</h1>
145
- <button onClick={increment}>+</button>
146
- <button onClick={decrement}>-</button>
147
- </div>
148
- );
45
+ const { count, increment } = useStore({ initial: 10 })
46
+ return <button onClick={increment}>{count}</button>
149
47
  }
150
48
  ```
151
49
 
152
- > ℹ️ `AutoRootCtx` accepts optional `Wrapper` and `debugging` props. Pass an ErrorBoundary-like component through `Wrapper` to isolate failures, or set `debugging` to `true` to render raw state snapshots in the DOM (handy alongside React DevTools when tracking updates).
50
+ **That's it.** No `Provider` wrapping per store. No complex setup. Just hooks.
153
51
 
154
- `useStore` keeps `Counter` focused on `count`, so even if this context grows with more fields later, the component only re-renders when `count` changes.
155
-
156
- **That's it!** No reducers, no actions, no providers to wrap—just hooks.
52
+ ---
157
53
 
158
- ## Core Concepts in Plain English
54
+ ## 🚀 Why React State Custom?
159
55
 
160
- - **Contexts on demand** `Context` extends `EventTarget`, so every state update is just an event dispatch. `getContext` memoizes instances per name and `useDataContext` automatically bumps a counter so unused contexts self-evict shortly after the last consumer unmounts.
161
- - **Publishers** – `useDataSource` and `useDataSourceMultiple` publish inside effects to keep renders pure. A registry guards against duplicate publishers fighting over the same key so you get actionable errors instead of stale data.
162
- - **Subscribers** – `useDataSubscribe*` hooks cover single, multiple, debounced, and transformed reads. `useQuickSubscribe` proxies the backing data object so each component subscribes only to the properties it touches.
163
- - **Root factories** – `createRootCtx` runs your headless hook exactly once per parameter set, publishes every returned key, and throws if two roots try to mount with the same resolved name. Your hook receives `(props, preState)` so it can rehydrate from the last published values when a root remounts. Parameters are serialized via `paramsToId`, so stick to primitive props (string/number/boolean/bigint/null/undefined) to keep IDs deterministic.
164
- - **Composable Stores** – Because stores are just hooks, you can subscribe to one store *inside* the logic of another. This enables powerful reactive chains where a derived store automatically updates whenever its upstream dependencies change.
165
- - **Auto orchestration** – Mount `<AutoRootCtx />` once and wire each root through `createAutoCtx`. The auto root listens for subscription requests, mounts/destroys the corresponding root on demand, and optionally keeps them alive for a configurable `timeToClean` window to smooth thrashing.
166
- - **Dev tooling** – `DevToolContainer` watches the memoized context cache, flashes updates in place, and lets you plug in custom renderers so you can diff state right beside your UI.
56
+ Most state libraries force you to learn a new way to write logic (reducers, atoms, proxies). **React State Custom** lets you use the React skills you already have.
167
57
 
168
- ## Core Building Blocks (copy & paste ready)
58
+ ### 💎 Zero Boilerplate
59
+ Define state with `useState`, `useEffect`, `useMemo`. No new syntax to learn.
169
60
 
170
- Familiarity beats theory, so here are the primitives you’ll reach for most often:
61
+ ### 🎯 Selective Re-renders
62
+ Components only re-render when the specific data they use changes. Performance is built-in.
171
63
 
172
- ### 1. Context – event-driven store
173
- ```typescript
174
- const ctx = useDataContext<MyState>('my-state');
175
- ```
64
+ ### 🔄 Automatic Lifecycle
65
+ Stores are created when needed and destroyed when unused. No more manual cleanup or memory leaks.
176
66
 
177
- ### 2. Data source – publish values
178
- ```typescript
179
- useDataSource(ctx, 'count', count);
180
- ```
67
+ ### 🛡️ TypeScript First
68
+ Full type inference out of the box. Your IDE knows exactly what's in your store.
181
69
 
182
- ### 3. Subscribers – pick exact fields
183
- ```typescript
184
- const count = useDataSubscribe(ctx, 'count');
185
- const { count, name } = useDataSubscribeMultiple(ctx, 'count', 'name');
186
- ```
70
+ ---
187
71
 
188
- ### 4. Root context – run your hook once
189
- ```typescript
190
- const { Root, useCtxState } = createRootCtx('my-state', useMyState);
191
- ```
72
+ ## 🛠️ Quick Start
192
73
 
193
- ### 5. Auto context – mount roots for you
194
- ```typescript
195
- const { useCtxState } = createAutoCtx(rootContext);
196
- ```
74
+ ### 1. Define Your State
75
+ Write a hook that returns the data and actions you want to share.
197
76
 
198
- ### 6. Store factory – all in one
199
77
  ```typescript
200
- const { useStore } = createStore('my-state', useMyState);
201
- ```
78
+ // features/userState.ts
79
+ import { useState, useEffect } from 'react'
202
80
 
203
- ### 6. Store factory all in one
204
- ```typescript
205
- const { useStore } = createStore('my-state', useMyState);
206
- ```
207
-
208
- ## 🎯 Key Features
209
-
210
- ### 1. **Just React Hooks**
211
- Use `useState`, `useEffect`, `useMemo`, and any other React hooks you already know. No new concepts to learn.
212
-
213
- ```typescript
214
- function useUserState({ userId }: { userId: string }) {
215
- const [user, setUser] = useState(null);
216
- const [loading, setLoading] = useState(true);
81
+ export const useUserState = ({ userId }: { userId: string }) => {
82
+ const [user, setUser] = useState(null)
217
83
 
218
84
  useEffect(() => {
219
- fetchUser(userId).then(setUser).finally(() => setLoading(false));
220
- }, [userId]);
221
-
222
- return { user, loading };
85
+ fetchUser(userId).then(setUser)
86
+ }, [userId])
87
+
88
+ return { user, isLoading: !user }
223
89
  }
224
90
  ```
225
91
 
226
- ### 2. **Selective Re-renders**
227
- Components only re-render when the **specific data they subscribe to** changes—not when anything in the state changes.
92
+ ### 2. Create the Store
93
+ Use `createStore` to generate a hook for your components.
228
94
 
229
95
  ```typescript
230
- // Only re-renders when 'user' changes, not when 'loading' changes
231
- const { user } = useDataSubscribeMultiple(ctx, 'user');
96
+ import { createStore } from 'react-state-custom'
97
+ import { useUserState } from './features/userState'
232
98
 
233
- // Or subscribe to multiple fields
234
- const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
99
+ export const { useStore: useUserStore } = createStore('user', useUserState)
235
100
  ```
236
101
 
237
- > ⚠️ `useQuickSubscribe` proxies are only readable during render. Destructure the properties you need immediately and avoid storing the proxy in refs, effects, or callbacks.
238
-
239
- ### 3. **Automatic Context Management**
240
- With `AutoRootCtx`, state contexts are automatically created and destroyed as needed. Mount it once near your application root, optionally providing a `Wrapper` (for error boundaries) or enabling `debugging` to render live state snapshots in the DOM—useful context when pairing with React DevTools. No manual provider management required.
241
-
242
- ### 4. **TypeScript First**
243
- Full type inference and type safety throughout. Your IDE knows exactly what's in your state.
244
-
245
- ### 5. **Tiny Bundle Size**
246
- ~10KB gzipped. No dependencies except React.
247
-
248
- ## 🆚 Comparison with Hooks, Redux & Zustand
249
-
250
- | Feature | React State Custom | Plain Hooks (Context) | Redux | Zustand |
251
- |---------|-------------------|-----------------------|-------|---------|
252
- | **Bundle Size** | ~10KB | 0KB (just React) | ~50KB (with toolkit) | ~1KB |
253
- | **Learning Curve** | ✅ Minimal (just hooks) | ⚠️ Familiar APIs, but patterns are DIY | ❌ High (actions, reducers, middleware) | ✅ Low |
254
- | **Boilerplate** | ✅ None | ❌ Manual providers + prop drilling | ❌ Heavy | ✅ Minimal |
255
- | **Type Safety** | ✅ Full inference | ⚠️ Custom per-context typing | ⚠️ Requires setup | ✅ Good |
256
- | **Selective Re-renders** | ✅ Built-in | ❌ Context update = every consumer renders | ⚠️ Requires selectors | ✅ Built-in |
257
- | **DevTools** | ✅ Built-in UI | ❌ None | ✅ Redux DevTools | ✅ DevTools support |
258
- | **Async Support** | ✅ Native (hooks) | ✅ Native (hooks) | ⚠️ Requires middleware | ✅ Native |
259
- | **Context Composition** | ✅ Automatic | ❌ Manual provider trees | ❌ Manual | ⚠️ Manual store combination |
260
-
261
- ### When to Use React State Custom
262
-
263
- ✅ **Choose React State Custom if you:**
264
- - Want to use React hooks for state management without learning new patterns
265
- - Need fine-grained control over component re-renders
266
- - Prefer minimal boilerplate and configuration
267
- - Want automatic context lifecycle management
268
- - Need multiple independent state contexts that don't interfere
269
-
270
- ❌ **Consider Redux if you:**
271
- - Need powerful time-travel debugging (Redux DevTools)
272
- - Have a very large team that benefits from strict architectural patterns
273
- - Already have significant Redux investment
274
-
275
- ❌ **Consider Zustand if you:**
276
- - Want the absolute smallest bundle size
277
- - Need a simple global store without context isolation
278
- - Don't need automatic context lifecycle management
279
-
280
- ## 🔥 Real-World Example: User Authentication
281
-
282
- ```typescript
283
- // authState.ts
284
- function useAuthState() {
285
- const [user, setUser] = useState<User | null>(null);
286
- const [loading, setLoading] = useState(true);
287
-
288
- useEffect(() => {
289
- // Check authentication on mount
290
- checkAuth().then(setUser).finally(() => setLoading(false));
291
- }, []);
292
-
293
- const login = async (email: string, password: string) => {
294
- setLoading(true);
295
- try {
296
- const user = await authService.login(email, password);
297
- setUser(user);
298
- } finally {
299
- setLoading(false);
300
- }
301
- };
302
-
303
- const logout = async () => {
304
- await authService.logout();
305
- setUser(null);
306
- };
307
-
308
- return { user, loading, login, logout };
309
- }
310
-
311
- export const { useStore: useAuthStore } = createStore('auth', useAuthState);
102
+ ### 3. Mount the Root (Once)
103
+ Add `<AutoRootCtx />` to your app's root. This component manages all your stores automatically.
312
104
 
105
+ ```tsx
313
106
  // App.tsx
314
- function App() {
107
+ import { AutoRootCtx } from 'react-state-custom'
108
+
109
+ export default function App() {
315
110
  return (
316
111
  <>
317
112
  <AutoRootCtx />
318
- <Router>
319
- <Header />
320
- <Routes />
321
- </Router>
113
+ <YourAppContent />
322
114
  </>
323
- );
324
- }
325
-
326
- // Header.tsx - Only re-renders when user changes
327
- function Header() {
328
- const { user, logout } = useAuthStore({});
329
-
330
- return (
331
- <header>
332
- {user ? (
333
- <>
334
- <span>Welcome, {user.name}</span>
335
- <button onClick={logout}>Logout</button>
336
- </>
337
- ) : (
338
- <Link to="/login">Login</Link>
339
- )}
340
- </header>
341
- );
342
- }
343
-
344
- // ProtectedRoute.tsx - Only re-renders when loading or user changes
345
- function ProtectedRoute({ children }) {
346
- const ctx = useAuthStore.useCtxState({});
347
- const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
348
-
349
- if (loading) return <Spinner />;
350
- if (!user) return <Navigate to="/login" />;
351
-
352
- return children;
115
+ )
353
116
  }
354
117
  ```
355
118
 
356
- **Compare with Redux:**
357
- ```typescript
358
- // Redux requires: action types, action creators, reducers, thunks/sagas
359
- // React State Custom: just write a hook! ✨
360
- ```
361
-
362
- ## 🚀 Advanced Features
363
-
364
- Once you have a store running, layer in these power-ups as needed.
119
+ ### 🎭 Isolated State
365
120
 
366
- ### Developer Tools
367
- Visual debugging component to inspect all your context data in real-time:
121
+ Need to run multiple independent instances of your application or isolate features? Use `StateScopeProvider`.
368
122
 
369
- ```typescript
370
- import { DevToolContainer } from 'react-state-custom';
371
- import 'react-state-custom/dist/react-state-custom.css';
123
+ ```tsx
124
+ import { AutoRootCtx, StateScopeProvider } from 'react-state-custom'
372
125
 
373
126
  function App() {
374
127
  return (
375
128
  <>
376
- <AutoRootCtx />
377
- <YourAppContent />
378
- <DevToolContainer />
129
+ <AutoRootCtx /> {/* Global Scope */}
130
+ <MainApp />
131
+
132
+ <StateScopeProvider>
133
+ {/* Isolated Scope - Stores here are independent of Global Scope */}
134
+ <IsolatedFeature />
135
+ </StateScopeProvider>
379
136
  </>
380
- );
137
+ )
381
138
  }
382
139
  ```
383
140
 
384
- The toggle reveals a bottom-docked inspector that now uses resizable split panes powered by `@uiw/react-split`. Drag the gutter to adjust how much space the context list or detail view occupies while keeping your application visible above.
385
-
386
- **Custom data viewer with rich object visualization:**
387
- ```typescript
388
- import { DataViewComponent } from 'react-state-custom';
389
- import { ObjectView } from 'react-obj-view';
390
- import 'react-obj-view/dist/react-obj-view.css'; // Required for ObjectView styling
141
+ Stores used inside `StateScopeProvider` will be completely isolated from the parent or global scope, even if they share the same store definition.
391
142
 
392
- const CustomDataView: DataViewComponent = ({ name, value }) => {
393
- return <ObjectView name={name} value={value} expandLevel={2} />;
394
- };
395
-
396
- <DevToolContainer Component={CustomDataView} />
397
- ```
398
-
399
- Pass `children` to `DevToolContainer` to customize the floating toggle button label (for example `<DevToolContainer>State Inspector</DevToolContainer>`), and import `react-state-custom/dist/react-state-custom.css` once to pick up the overlay styles.
400
-
401
- ### Parameterized Contexts
402
- Create multiple instances of the same state with different parameters:
403
-
404
- ```typescript
405
- function useUserState({ userId }: { userId: string }) {
406
- // State logic here
407
- }
408
-
409
- const { useStore: useUserStore } = createStore('user', useUserState);
143
+ ---
410
144
 
411
- // Different instances for different users
412
- function UserProfile({ userId }) {
413
- const { user } = useUserStore({ userId }); // Automatic instance per userId
414
- return <div>{user?.name}</div>;
415
- }
416
- ```
145
+ ## 🆚 Comparison
417
146
 
418
- > Need to avoid rapid mount/unmount churn? Pass a second argument to `createStore` (for example `createStore('user', useUserState, 200)`) to keep instances alive for a few extra milliseconds before disposal.
147
+ | Feature | React State Custom | Redux | Context API | Zustand |
148
+ |:---|:---:|:---:|:---:|:---:|
149
+ | **Paradigm** | Just Hooks 🪝 | Actions/Reducers | Providers | Store Object |
150
+ | **Boilerplate** | 🟢 None | 🔴 High | 🟡 Medium | 🟢 Low |
151
+ | **Auto Lifecycle** | ✅ Yes | ❌ No | ❌ No | ❌ No |
152
+ | **Selective Renders** | ✅ Automatic | ⚠️ Selectors | ❌ Manual | ✅ Selectors |
153
+ | **Learning Curve** | 🟢 Low | 🔴 High | 🟡 Medium | 🟢 Low |
419
154
 
420
- > ⚠️ The props you pass to `createStore`/`useStore` must be composed of primitive values (string, number, boolean, bigint, null, or undefined). Objects are rejected so context names stay deterministic—pass IDs instead of raw objects.
155
+ ---
421
156
 
422
- ### Debounced Subscriptions
423
- Optimize performance for frequently changing values:
157
+ ## 🧩 Advanced Features
424
158
 
425
- ```typescript
426
- // Re-render at most once per 300ms
427
- const searchQuery = useDataSubscribe(ctx, 'searchQuery', 300);
428
- ```
159
+ ### 🔌 Developer Tools
160
+ Inspect your state in real-time with the built-in DevTools.
429
161
 
430
- ### Transformed Subscriptions
431
- Transform data before using it:
162
+ ```tsx
163
+ import { DevToolContainer } from 'react-state-custom'
164
+ import 'react-state-custom/dist/react-state-custom.css'
432
165
 
433
- ```typescript
434
- const userStats = useDataSubscribeWithTransform(
435
- ctx,
436
- 'user',
437
- (user) => ({
438
- fullName: `${user?.firstName} ${user?.lastName}`,
439
- isAdmin: user?.role === 'admin'
440
- })
441
- );
166
+ <DevToolContainer />
442
167
  ```
443
168
 
444
- ### Composing Stores (Derived State)
445
- Since stores are just hooks, you can subscribe to one store *inside* another. This allows you to build reactive dependency chains where a downstream store automatically updates when an upstream store changes.
169
+ ### 🆔 Parameterized Stores
170
+ Create multiple independent instances of the same store by passing different parameters.
446
171
 
447
- ```typescript
448
- // 1. Upstream Store
449
- const { useStore: useUserStore } = createStore('user', () => {
450
- const [role, setRole] = useState('guest');
451
- return { role, setRole };
452
- });
453
-
454
- // 2. Downstream Store (depends on User)
455
- const useDashboardStore = () => {
456
- // Subscribe to the upstream store
457
- const { role } = useUserStore({});
458
-
459
- // Derive state based on the upstream value
460
- const permissions = useMemo(() => {
461
- return role === 'admin' ? ['read', 'write', 'delete'] : ['read'];
462
- }, [role]);
463
-
464
- return { permissions };
465
- };
466
-
467
- const { useStore: useDashboardStore } = createStore('dashboard', useDashboardStore);
172
+ ```tsx
173
+ // Creates a unique store for each ID
174
+ const { count } = useStore({ id: 'counter-1' })
175
+ const { count } = useStore({ id: 'counter-2' })
468
176
  ```
469
177
 
470
- ## 🎮 Live Examples
471
-
472
- Explore interactive examples in the **[Live Demo](https://vothanhdat.github.io/react-state-custom/)**:
473
-
474
- - **Counter** - Basic state management with increment, decrement, and reset
475
- - **Todo List** - Multiple independent lists with scoped contexts
476
- - **Form Validation** - Real-time validation with error handling
477
- - **Timer** - Side effects and cleanup with millisecond precision
478
- - **Shopping Cart** - Complex state with derived values (total, itemCount)
479
-
480
- Each example includes live code editing with syntax highlighting, powered by Sandpack!
481
-
482
- ## 📖 Documentation
483
-
484
- For complete API documentation, examples, and advanced patterns, see:
485
- - **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference
486
- - **[Live Demo](https://vothanhdat.github.io/react-state-custom/)** - Interactive examples
487
-
488
- ## 🛠️ Development
178
+ ### ⚡️ Derived State
179
+ Compose stores just like hooks.
489
180
 
490
- ```bash
491
- # Install dependencies
492
- yarn install
493
-
494
- # Run development UI with example selector
495
- yarn dev
496
-
497
- # Run interactive playground with live code editing
498
- yarn dev:playground
499
-
500
- # Build library
501
- yarn build
502
-
503
- # Build demo site
504
- yarn build:demo
505
-
506
- # Preview demo locally
507
- yarn preview
181
+ ```tsx
182
+ const useCartTotal = () => {
183
+ const { items } = useCartStore({})
184
+ return items.reduce((total, item) => total + item.price, 0)
185
+ }
508
186
  ```
509
187
 
510
- ### Development Modes
511
-
512
- **`yarn dev`** - Starts a clean development UI with an interactive example selector. Great for:
513
- - Testing all examples in one place
514
- - Quick switching between different examples
515
- - Visual debugging with DevTool component
516
-
517
- **`yarn dev:playground`** - Starts the Sandpack-powered playground with live code editing. Perfect for:
518
- - Creating interactive demos
519
- - Live code editing and experimentation
520
- - Sharing editable examples
521
-
522
- ## 🎓 Learning Path
523
-
524
- 1. **Follow the Quick Start** – build one shared store end-to-end.
525
- 2. **Layer on subscriptions** – swap `useQuickSubscribe` for the more specific `useDataSubscribe*` hooks where it makes sense.
526
- 3. **Optimize when needed** – introduce debounced/transform subscriptions and `createAutoCtx` grace periods to smooth noisy stores.
527
- 4. **Scale up** – add parameterized contexts (one store per ID) and wire the DevTool overlay for visibility.
188
+ ---
528
189
 
529
190
  ## 📦 Installation
530
191
 
@@ -532,18 +193,13 @@ yarn preview
532
193
  npm install react-state-custom
533
194
  # or
534
195
  yarn add react-state-custom
535
- # or
536
- pnpm add react-state-custom
537
196
  ```
538
197
 
539
- ## 🤝 Contributing
198
+ ## 📖 Documentation
540
199
 
541
- Contributions are welcome! Please feel free to submit a Pull Request.
200
+ - **[API Reference](./API_DOCUMENTATION.md)** - Full API documentation.
201
+ - **[Live Demo](https://vothanhdat.github.io/react-state-custom/)** - Interactive examples.
542
202
 
543
203
  ## 📄 License
544
204
 
545
- MIT License - feel free to use in any project.
546
-
547
- ---
548
-
549
- **Made with ❤️ for developers who love React hooks**
205
+ MIT © Vo Thanh Dat
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Context, getContext, useDataContext, useDataSource, useDataSourceMultiple, useDataSubscribe, useDataSubscribeMultiple, useDataSubscribeMultipleWithDebounce, useDataSubscribeWithTransform } from './state-utils/ctx';
2
2
  export { createRootCtx } from './state-utils/createRootCtx';
3
- export { AutoRootCtx, createAutoCtx, createStore } from './state-utils/createAutoCtx';
3
+ export { AutoRootCtx, createAutoCtx, createStore, StateScopeProvider } from './state-utils/createAutoCtx';
4
4
  export { useArrayChangeId } from './state-utils/useArrayChangeId';
5
5
  export { paramsToId, type ParamsToIdRecord, type ParamsToIdInput } from './state-utils/paramsToId';
6
6
  export { useQuickSubscribe } from './state-utils/useQuickSubscribe';