react-state-custom 1.0.29 → 1.0.31
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 +97 -34
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +232 -225
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +4 -4
- package/dist/index.umd.js.map +1 -1
- package/dist/state-utils/createAutoCtx.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Simple. Powerful. TypeScript-first.**
|
|
4
4
|
|
|
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.
|
|
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.
|
|
6
6
|
|
|
7
7
|
[](https://vothanhdat.github.io/react-state-custom/)
|
|
8
8
|
[](https://www.npmjs.com/package/react-state-custom)
|
|
@@ -14,11 +14,55 @@ 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)
|
|
18
|
+
|
|
19
|
+
If you already know how to write a component with `useState`, you're moments away from sharing that state everywhere.
|
|
20
|
+
|
|
21
|
+
1. **Write a plain hook** – encapuslate data fetching, derived values, and actions inside a normal React hook.
|
|
22
|
+
2. **Create a root context** – `createRootCtx('feature', useFeatureState)` publishes the hook output into a context namespace.
|
|
23
|
+
3. **Let AutoRoot manage lifecycles** – `createAutoCtx(rootCtx, 150)` registers the store with `<AutoRootCtx />` and (optionally) keeps it alive for a short grace period after the last subscriber unmounts.
|
|
24
|
+
4. **Mount `<AutoRootCtx />` once** – drop it near the top of your tree (wrap it with your own `ErrorBoundary` if desired).
|
|
25
|
+
5. **Consume anywhere** – call the generated `useCtxState` hook and destructure data via `useQuickSubscribe` or any `useDataSubscribe*` helper.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
const useFeatureState = ({ featureId }: { featureId: string }) => {
|
|
29
|
+
const [value, setValue] = useState(0)
|
|
30
|
+
const double = useMemo(() => value * 2, [value])
|
|
31
|
+
return { value, double, increment: () => setValue(v => v + 1) }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const featureRoot = createRootCtx('feature', useFeatureState)
|
|
35
|
+
export const { useCtxState: useFeatureCtx } = createAutoCtx(featureRoot, 250)
|
|
36
|
+
|
|
37
|
+
function AppShell() {
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<AutoRootCtx Wrapper={ErrorBoundary} debugging={import.meta.env.DEV} />
|
|
41
|
+
<Routes />
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function FeatureMeter({ featureId }: { featureId: string }) {
|
|
47
|
+
const ctx = useFeatureCtx({ featureId })
|
|
48
|
+
const { value, double, increment } = useQuickSubscribe(ctx)
|
|
49
|
+
return (
|
|
50
|
+
<section>
|
|
51
|
+
<strong>{value}</strong>
|
|
52
|
+
<em>{double}</em>
|
|
53
|
+
<button onClick={increment}>Add</button>
|
|
54
|
+
</section>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That’s the entire workflow—no reducers, actions, or provider trees.
|
|
60
|
+
|
|
17
61
|
## Why React State Custom?
|
|
18
62
|
|
|
19
63
|
**Zero Boilerplate** • **Type-Safe** • **Selective Re-renders** • **Hook-Based** • **~10KB Bundle**
|
|
20
64
|
|
|
21
|
-
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.
|
|
65
|
+
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.
|
|
22
66
|
|
|
23
67
|
### When `useState` + `useEffect` Fall Short
|
|
24
68
|
|
|
@@ -113,6 +157,45 @@ function Counter() {
|
|
|
113
157
|
|
|
114
158
|
**That's it!** No reducers, no actions, no providers to wrap—just hooks.
|
|
115
159
|
|
|
160
|
+
## Core Concepts in Plain English
|
|
161
|
+
|
|
162
|
+
- **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.
|
|
163
|
+
- **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.
|
|
164
|
+
- **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.
|
|
165
|
+
- **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. Parameters are serialized via `paramsToId`, so stick to primitive props (string/number/boolean/bigint/null/undefined) to keep IDs deterministic.
|
|
166
|
+
- **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.
|
|
167
|
+
- **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.
|
|
168
|
+
|
|
169
|
+
## Core Building Blocks (copy & paste ready)
|
|
170
|
+
|
|
171
|
+
Familiarity beats theory, so here are the primitives you’ll reach for most often:
|
|
172
|
+
|
|
173
|
+
### 1. Context – event-driven store
|
|
174
|
+
```typescript
|
|
175
|
+
const ctx = useDataContext<MyState>('my-state');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 2. Data source – publish values
|
|
179
|
+
```typescript
|
|
180
|
+
useDataSource(ctx, 'count', count);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 3. Subscribers – pick exact fields
|
|
184
|
+
```typescript
|
|
185
|
+
const count = useDataSubscribe(ctx, 'count');
|
|
186
|
+
const { count, name } = useDataSubscribeMultiple(ctx, 'count', 'name');
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 4. Root context – run your hook once
|
|
190
|
+
```typescript
|
|
191
|
+
const { Root, useCtxState } = createRootCtx('my-state', useMyState);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 5. Auto context – mount roots for you
|
|
195
|
+
```typescript
|
|
196
|
+
const { useCtxState } = createAutoCtx(rootContext);
|
|
197
|
+
```
|
|
198
|
+
|
|
116
199
|
## 🎯 Key Features
|
|
117
200
|
|
|
118
201
|
### 1. **Just React Hooks**
|
|
@@ -142,6 +225,8 @@ const { user } = useDataSubscribeMultiple(ctx, 'user');
|
|
|
142
225
|
const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
|
|
143
226
|
```
|
|
144
227
|
|
|
228
|
+
> ⚠️ `useQuickSubscribe` proxies are only readable during render. Destructure the properties you need immediately and avoid storing the proxy in refs, effects, or callbacks.
|
|
229
|
+
|
|
145
230
|
### 3. **Automatic Context Management**
|
|
146
231
|
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.
|
|
147
232
|
|
|
@@ -268,36 +353,10 @@ function ProtectedRoute({ children }) {
|
|
|
268
353
|
// React State Custom: just write a hook! ✨
|
|
269
354
|
```
|
|
270
355
|
|
|
271
|
-
## 📚 Core Concepts
|
|
272
|
-
|
|
273
|
-
### 1. **Context** - Event-driven state container
|
|
274
|
-
```typescript
|
|
275
|
-
const ctx = useDataContext<MyState>('my-state');
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### 2. **Data Source** - Publish values to context
|
|
279
|
-
```typescript
|
|
280
|
-
useDataSource(ctx, 'count', count);
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### 3. **Data Subscription** - Subscribe to specific values
|
|
284
|
-
```typescript
|
|
285
|
-
const count = useDataSubscribe(ctx, 'count');
|
|
286
|
-
const { count, name } = useDataSubscribeMultiple(ctx, 'count', 'name');
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### 4. **Root Context** - Lifecycle-managed context
|
|
290
|
-
```typescript
|
|
291
|
-
const { Root, useCtxState } = createRootCtx('my-state', useMyState);
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### 5. **Auto Context** - Automatic instance management
|
|
295
|
-
```typescript
|
|
296
|
-
const { useCtxState } = createAutoCtx(rootContext);
|
|
297
|
-
```
|
|
298
|
-
|
|
299
356
|
## 🚀 Advanced Features
|
|
300
357
|
|
|
358
|
+
Once you have a store running, layer in these power-ups as needed.
|
|
359
|
+
|
|
301
360
|
### Developer Tools
|
|
302
361
|
Visual debugging component to inspect all your context data in real-time:
|
|
303
362
|
|
|
@@ -331,6 +390,8 @@ const CustomDataView: DataViewComponent = ({ name, value }) => {
|
|
|
331
390
|
<DevToolContainer Component={CustomDataView} />
|
|
332
391
|
```
|
|
333
392
|
|
|
393
|
+
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.
|
|
394
|
+
|
|
334
395
|
### Parameterized Contexts
|
|
335
396
|
Create multiple instances of the same state with different parameters:
|
|
336
397
|
|
|
@@ -353,6 +414,8 @@ function UserProfile({ userId }) {
|
|
|
353
414
|
|
|
354
415
|
> Need to avoid rapid mount/unmount churn? Pass a second argument to `createAutoCtx` (for example `createAutoCtx(rootCtx, 200)`) to keep instances alive for a few extra milliseconds before disposal.
|
|
355
416
|
|
|
417
|
+
> ⚠️ The props you pass to `createRootCtx`/`useCtxState` 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.
|
|
418
|
+
|
|
356
419
|
### Debounced Subscriptions
|
|
357
420
|
Optimize performance for frequently changing values:
|
|
358
421
|
|
|
@@ -429,10 +492,10 @@ yarn preview
|
|
|
429
492
|
|
|
430
493
|
## 🎓 Learning Path
|
|
431
494
|
|
|
432
|
-
1. **
|
|
433
|
-
2. **
|
|
434
|
-
3. **Optimize**
|
|
435
|
-
4. **Scale**
|
|
495
|
+
1. **Follow the Quick Start** – build one shared store end-to-end.
|
|
496
|
+
2. **Layer on subscriptions** – swap `useQuickSubscribe` for the more specific `useDataSubscribe*` hooks where it makes sense.
|
|
497
|
+
3. **Optimize when needed** – introduce debounced/transform subscriptions and `createAutoCtx` grace periods to smooth noisy stores.
|
|
498
|
+
4. **Scale up** – add parameterized contexts (one store per ID) and wire the DevTool overlay for visibility.
|
|
436
499
|
|
|
437
500
|
## 📦 Installation
|
|
438
501
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { Context, getContext, useDataContext, useDataSource, useDataSourceMultip
|
|
|
2
2
|
export { createRootCtx } from './state-utils/createRootCtx';
|
|
3
3
|
export { AutoRootCtx, createAutoCtx } from './state-utils/createAutoCtx';
|
|
4
4
|
export { useArrayChangeId } from './state-utils/useArrayChangeId';
|
|
5
|
+
export { paramsToId, type ParamsToIdRecord, type ParamsToIdInput } from './state-utils/paramsToId';
|
|
5
6
|
export { useQuickSubscribe } from './state-utils/useQuickSubscribe';
|
|
6
7
|
export { DevToolContainer } from './dev-tool/DevTool';
|
|
7
8
|
export type { DataViewComponent } from './dev-tool/DataViewComponent';
|