react-global-state-hooks 16.0.5 โ 16.0.6
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 +215 -144
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,21 +8,23 @@
|
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
11
|
-
**Shared
|
|
11
|
+
**Shared state that feels like `useState`.**
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Share state across your React application with the familiar `useState` API, subscribe to only the slice a component needs, and grow into typed actions, chainable selectors, and scoped stores. Persist what matters in `localStorage`. Inspect every store, action, and state change in the DevTools panel, or from your terminal.
|
|
14
14
|
|
|
15
15
|
[](https://www.npmjs.com/package/react-global-state-hooks)
|
|
16
16
|
[](https://www.npmjs.com/package/react-global-state-hooks)
|
|
17
17
|
[](https://github.com/johnny-quesada-developer/react-global-state-hooks/blob/master/LICENSE)
|
|
18
18
|
|
|
19
|
-
[**
|
|
19
|
+
[**Website**](https://johnny-quesada-developer.github.io/react-global-state-hooks/) ยท [**Documentation**](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/) ยท [**Examples**](https://johnny-quesada-developer.github.io/react-global-state-hooks/examples/) ยท [**DevTools**](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/devtools/) ยท [**Agentic DevTools**](https://johnny-quesada-developer.github.io/react-global-state-hooks/agentic-devtools/)
|
|
20
|
+
|
|
21
|
+
Created by [Johnny Quesada](https://johnny-quesada-developer.github.io/react-global-state-hooks/about/), author of the React Global State Hooks family.
|
|
20
22
|
|
|
21
23
|
</div>
|
|
22
24
|
|
|
23
25
|
---
|
|
24
26
|
|
|
25
|
-
##
|
|
27
|
+
## Start with a single hook
|
|
26
28
|
|
|
27
29
|
```tsx
|
|
28
30
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
@@ -30,7 +32,7 @@ import { createGlobalState } from 'react-global-state-hooks';
|
|
|
30
32
|
export const useCounter = createGlobalState(0);
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
A shared store, ready to use. Global hooks work without a provider; use `createContext` when a subtree needs its own instance.
|
|
34
36
|
|
|
35
37
|
```tsx
|
|
36
38
|
// Use it anywhere, instantly
|
|
@@ -42,35 +44,42 @@ function Counter() {
|
|
|
42
44
|
|
|
43
45
|
---
|
|
44
46
|
|
|
45
|
-
##
|
|
47
|
+
## Built for React applications
|
|
48
|
+
|
|
49
|
+
### **Familiar React API**
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
If you know `useState`, you already know the API.
|
|
48
52
|
|
|
49
53
|
```tsx
|
|
50
|
-
//
|
|
51
|
-
const [
|
|
54
|
+
// Local state
|
|
55
|
+
const [count, setCount] = useState(0);
|
|
52
56
|
|
|
53
|
-
//
|
|
54
|
-
const [
|
|
57
|
+
// Shared state
|
|
58
|
+
const [count, setCount] = useCounter();
|
|
55
59
|
```
|
|
56
60
|
|
|
57
|
-
###
|
|
61
|
+
### **Precise Subscriptions**
|
|
58
62
|
|
|
59
|
-
Subscribe to a slice so unrelated store changes do not
|
|
63
|
+
Subscribe to a slice, so unrelated store changes do not re-render your component.
|
|
60
64
|
|
|
61
65
|
```tsx
|
|
62
66
|
// Only re-renders when name changes
|
|
63
|
-
const [name] = useStore((
|
|
67
|
+
const [name] = useStore((state) => state.user.name);
|
|
64
68
|
```
|
|
65
69
|
|
|
66
|
-
###
|
|
70
|
+
### **Chainable Selectors**
|
|
71
|
+
|
|
72
|
+
Build reusable state hooks on top of other selector hooks.
|
|
67
73
|
|
|
68
74
|
```tsx
|
|
69
|
-
const useUsers = useStore.createSelectorHook((
|
|
70
|
-
|
|
75
|
+
const useUsers = useStore.createSelectorHook((state) => state.users);
|
|
76
|
+
|
|
77
|
+
const useAdmins = useUsers.createSelectorHook((users) => users.filter((user) => user.isAdmin));
|
|
71
78
|
```
|
|
72
79
|
|
|
73
|
-
###
|
|
80
|
+
### **Actions (Optional)**
|
|
81
|
+
|
|
82
|
+
Keep mutation logic next to the store when a feature needs more structure.
|
|
74
83
|
|
|
75
84
|
```tsx
|
|
76
85
|
const useAuth = createGlobalState(null, {
|
|
@@ -78,6 +87,7 @@ const useAuth = createGlobalState(null, {
|
|
|
78
87
|
login(credentials) {
|
|
79
88
|
return async ({ setState }) => {
|
|
80
89
|
const user = await api.login(credentials);
|
|
90
|
+
|
|
81
91
|
setState(user);
|
|
82
92
|
};
|
|
83
93
|
},
|
|
@@ -85,7 +95,9 @@ const useAuth = createGlobalState(null, {
|
|
|
85
95
|
});
|
|
86
96
|
```
|
|
87
97
|
|
|
88
|
-
###
|
|
98
|
+
### **Context Mode**
|
|
99
|
+
|
|
100
|
+
Need an isolated instance instead of app-wide state? Same state model, inside a provider.
|
|
89
101
|
|
|
90
102
|
```tsx
|
|
91
103
|
const Form = createContext({ name: '', email: '' });
|
|
@@ -95,19 +107,59 @@ const Form = createContext({ name: '', email: '' });
|
|
|
95
107
|
</Form.Provider>;
|
|
96
108
|
```
|
|
97
109
|
|
|
98
|
-
###
|
|
110
|
+
### **Persistence with localStorage**
|
|
111
|
+
|
|
112
|
+
Keep preferences and drafts across browser sessions.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
const useSettings = createGlobalState(
|
|
116
|
+
{ theme: 'dark' },
|
|
117
|
+
{
|
|
118
|
+
localStorage: {
|
|
119
|
+
key: 'settings',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### **Non-Reactive API**
|
|
99
126
|
|
|
100
|
-
|
|
127
|
+
Read, update, or subscribe to state outside React components.
|
|
101
128
|
|
|
102
129
|
```tsx
|
|
103
|
-
// In API interceptors, WebSockets, utils...
|
|
104
130
|
const token = useAuth.getState().token;
|
|
105
|
-
|
|
131
|
+
|
|
132
|
+
useAuth.setState({ user: nextUser });
|
|
106
133
|
```
|
|
107
134
|
|
|
108
135
|
---
|
|
109
136
|
|
|
110
|
-
##
|
|
137
|
+
## Agentic DevTools
|
|
138
|
+
|
|
139
|
+
Give your coding agent the runtime, not a guess. With the [Chrome extension](https://chromewebstore.google.com/detail/bafojplmkpejhglhjpibpdhoblickpee) open on your app, the `rgsh` command that ships with this package reads store state, runs store actions and prints exactly which paths changed โ from the terminal your agent already uses.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install --save-dev ws # rgsh talks to the extension over a local WebSocket
|
|
143
|
+
npx rgsh --list # every store DevTools knows about, with its actions
|
|
144
|
+
npx rgsh action todos add "Write the docs" # run an action; prints each changed path, before โ after
|
|
145
|
+
npx rgsh state todos todos[0].done # read the result back
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Import the debug entry first in your **development** entry file, before any module that creates a store, and keep it out of production builds:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import 'react-global-state-hooks/debug';
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`rgsh` works on state and actions only; it does not click or type in the page. Take the [Agentic DevTools tour](https://johnny-quesada-developer.github.io/react-global-state-hooks/agentic-devtools/) for the inspect โ act โ verify workflow, and read the [DevTools guide](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/devtools/) for setup, the panel and the exit codes.
|
|
155
|
+
|
|
156
|
+
| The app being inspected | Stores, actions, and state diffs in the panel |
|
|
157
|
+
| ------------------------- | ----------------------------------------------- |
|
|
158
|
+
| <img alt="Debug playground app" src="https://johnny-quesada-developer.github.io/react-global-state-hooks/devtools/showcase/app.png" width="380"> | <img alt="DevTools panel" src="https://johnny-quesada-developer.github.io/react-global-state-hooks/devtools/showcase/panel.png" width="620"> |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Installation
|
|
111
163
|
|
|
112
164
|
```bash
|
|
113
165
|
npm install react-global-state-hooks
|
|
@@ -120,9 +172,9 @@ npm install react-global-state-hooks
|
|
|
120
172
|
|
|
121
173
|
---
|
|
122
174
|
|
|
123
|
-
##
|
|
175
|
+
## Quick Start
|
|
124
176
|
|
|
125
|
-
###
|
|
177
|
+
### Your first shared store
|
|
126
178
|
|
|
127
179
|
```tsx
|
|
128
180
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
@@ -195,13 +247,13 @@ function LoginButton({ authenticate }: { authenticate: () => Promise<User> }) {
|
|
|
195
247
|
|
|
196
248
|
---
|
|
197
249
|
|
|
198
|
-
##
|
|
250
|
+
## Core Features Deep Dive
|
|
199
251
|
|
|
200
|
-
###
|
|
252
|
+
### Global State with `createGlobalState`
|
|
201
253
|
|
|
202
|
-
|
|
254
|
+
Keep app-wide state outside the component tree and subscribe wherever it is needed.
|
|
203
255
|
|
|
204
|
-
####
|
|
256
|
+
#### The Basics
|
|
205
257
|
|
|
206
258
|
```tsx
|
|
207
259
|
// Primitives
|
|
@@ -224,7 +276,7 @@ const useExpensiveState = createGlobalState(() => {
|
|
|
224
276
|
});
|
|
225
277
|
```
|
|
226
278
|
|
|
227
|
-
####
|
|
279
|
+
#### Focused subscriptions with Selectors
|
|
228
280
|
|
|
229
281
|
Selectors keep components subscribed to the state they use.
|
|
230
282
|
|
|
@@ -273,7 +325,7 @@ function NotificationCount() {
|
|
|
273
325
|
| `useStore()` | Any store value change |
|
|
274
326
|
| `useStore(selector)` | A change to the selected value |
|
|
275
327
|
|
|
276
|
-
####
|
|
328
|
+
#### Computed Values with Dependencies
|
|
277
329
|
|
|
278
330
|
Derive values from both store state and component-local values, with explicit dependencies.
|
|
279
331
|
|
|
@@ -351,7 +403,7 @@ function AdvancedFiltered() {
|
|
|
351
403
|
}
|
|
352
404
|
```
|
|
353
405
|
|
|
354
|
-
####
|
|
406
|
+
#### Reusable Selector Hooks
|
|
355
407
|
|
|
356
408
|
Compose reusable selector hooks. Each hook returns its selected value directly.
|
|
357
409
|
|
|
@@ -416,7 +468,7 @@ function ActivateAll() {
|
|
|
416
468
|
}
|
|
417
469
|
```
|
|
418
470
|
|
|
419
|
-
**
|
|
471
|
+
**Selector composition:**
|
|
420
472
|
|
|
421
473
|
| Feature | Benefit |
|
|
422
474
|
| -------------------- | ------------------------------------------ |
|
|
@@ -426,9 +478,9 @@ function ActivateAll() {
|
|
|
426
478
|
| ๐จ **Clean Code** | No repetitive selector logic in components |
|
|
427
479
|
| โก **Performance** | Automatic memoization and change detection |
|
|
428
480
|
|
|
429
|
-
####
|
|
481
|
+
#### Actions - When You Need Structure
|
|
430
482
|
|
|
431
|
-
|
|
483
|
+
Add actions to give your store a clear, reusable mutation API.
|
|
432
484
|
|
|
433
485
|
```tsx
|
|
434
486
|
const useStore = createGlobalState(
|
|
@@ -519,15 +571,15 @@ function TodoApp() {
|
|
|
519
571
|
);
|
|
520
572
|
}
|
|
521
573
|
|
|
522
|
-
//
|
|
574
|
+
// Direct updates remain available through setState.
|
|
523
575
|
const handleQuickFix = () => {
|
|
524
576
|
useStore.setState((s) => ({ ...s, filter: 'all' }));
|
|
525
577
|
};
|
|
526
578
|
```
|
|
527
579
|
|
|
528
|
-
####
|
|
580
|
+
#### Store access outside React
|
|
529
581
|
|
|
530
|
-
|
|
582
|
+
Use the same store API in components, services, and background workflows.
|
|
531
583
|
|
|
532
584
|
```tsx
|
|
533
585
|
const useAuth = createGlobalState({ user: null, token: null });
|
|
@@ -569,9 +621,9 @@ const unsubscribe = useAuth.subscribe(
|
|
|
569
621
|
);
|
|
570
622
|
```
|
|
571
623
|
|
|
572
|
-
####
|
|
624
|
+
#### Observable Fragments
|
|
573
625
|
|
|
574
|
-
Create observable slices
|
|
626
|
+
Create observable state slices for subscriptions outside React.
|
|
575
627
|
|
|
576
628
|
```tsx
|
|
577
629
|
const useStore = createGlobalState({
|
|
@@ -597,7 +649,7 @@ console.log(countObservable.getState()); // Current count value
|
|
|
597
649
|
const doubledObservable = countObservable.createObservable((count) => count * 2);
|
|
598
650
|
```
|
|
599
651
|
|
|
600
|
-
####
|
|
652
|
+
#### Metadata - Non-Reactive Side Info
|
|
601
653
|
|
|
602
654
|
Keep request counters and timestamps in metadata when they are read only by handlers or background work.
|
|
603
655
|
Put loading indicators and displayed errors in state so components subscribe to their changes.
|
|
@@ -621,11 +673,11 @@ function recordFetchedItems(items: string[]) {
|
|
|
621
673
|
|
|
622
674
|
---
|
|
623
675
|
|
|
624
|
-
###
|
|
676
|
+
### Persisted State with localStorage
|
|
625
677
|
|
|
626
|
-
|
|
678
|
+
Keep preferences and drafts across browser sessions with built-in localStorage integration. Add `localStorage: { key }` to a store; the library handles saving and restoration.
|
|
627
679
|
|
|
628
|
-
###
|
|
680
|
+
### Basic Persistence
|
|
629
681
|
|
|
630
682
|
```tsx
|
|
631
683
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
@@ -640,30 +692,30 @@ const useSettings = createGlobalState(
|
|
|
640
692
|
);
|
|
641
693
|
```
|
|
642
694
|
|
|
643
|
-
|
|
695
|
+
With persistence enabled, the store will:
|
|
644
696
|
|
|
645
697
|
- โ
Save to localStorage on every change
|
|
646
698
|
- โ
Restore from localStorage on initialization
|
|
647
699
|
- โ
Handle JSON serialization/deserialization
|
|
648
|
-
- โ
|
|
700
|
+
- โ
Persist objects, arrays, and primitive values
|
|
649
701
|
|
|
650
|
-
###
|
|
702
|
+
### Safe Persistence with Validation (Optional)
|
|
651
703
|
|
|
652
704
|
The `validator` is **completely optional**. Add it only when you need to validate or transform restored data:
|
|
653
705
|
|
|
654
706
|
```tsx
|
|
655
707
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
708
|
+
import { z } from 'zod';
|
|
709
|
+
|
|
710
|
+
const contacts = z.map(z.string(), z.object({ email: z.string() }));
|
|
656
711
|
|
|
657
|
-
const useContacts = createGlobalState(new Map(), {
|
|
712
|
+
const useContacts = createGlobalState(new Map<string, { email: string }>(), {
|
|
658
713
|
localStorage: {
|
|
659
714
|
key: 'contacts',
|
|
660
715
|
|
|
661
|
-
// Optional: validate/transform restored data
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (!isMap(restored)) return initial;
|
|
665
|
-
return restored as typeof initial;
|
|
666
|
-
},
|
|
716
|
+
// Optional: validate/transform restored data. A schema library makes this one line, because a
|
|
717
|
+
// throw is caught by the store and the initial state is kept.
|
|
718
|
+
validator: ({ restored }) => contacts.parse(restored),
|
|
667
719
|
},
|
|
668
720
|
});
|
|
669
721
|
```
|
|
@@ -676,20 +728,31 @@ const useContacts = createGlobalState(new Map(), {
|
|
|
676
728
|
- Return a value โ That value becomes the new state
|
|
677
729
|
- Return `undefined` โ The restored data is used as-is
|
|
678
730
|
- Return `initial` โ Falls back to initial state (validation failed)
|
|
731
|
+
- Throw โ Same as returning `initial`, and the error reaches `onError`. This is what lets a schema
|
|
732
|
+
library be the whole validator
|
|
733
|
+
- Validate the state, not the fragment. `restored` holds only what the `selector` saved, so merge it over
|
|
734
|
+
`initial` first: `({ restored, initial }) => schema.parse({ ...initial, ...restored })`
|
|
679
735
|
|
|
680
736
|
**When to use validator:**
|
|
681
737
|
|
|
682
738
|
- โ
You changed your data structure and need to migrate old data
|
|
683
739
|
- โ
You want to validate data integrity
|
|
684
740
|
- โ
You need to transform restored data
|
|
685
|
-
|
|
741
|
+
For simple cases, you can omit the validator.
|
|
686
742
|
|
|
687
|
-
###
|
|
743
|
+
### Versioning & Migration (Optional)
|
|
688
744
|
|
|
689
745
|
Handle breaking changes gracefully with optional versioning:
|
|
690
746
|
|
|
691
747
|
```tsx
|
|
692
748
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
749
|
+
import { z } from 'zod';
|
|
750
|
+
|
|
751
|
+
const prefs = z.object({
|
|
752
|
+
theme: z.enum(['dark', 'light']),
|
|
753
|
+
notifications: z.boolean(),
|
|
754
|
+
language: z.string(),
|
|
755
|
+
});
|
|
693
756
|
|
|
694
757
|
const useUserPrefs = createGlobalState(
|
|
695
758
|
{ theme: 'dark', notifications: true, language: 'en' },
|
|
@@ -715,12 +778,7 @@ const useUserPrefs = createGlobalState(
|
|
|
715
778
|
},
|
|
716
779
|
|
|
717
780
|
// Optional: validate after migration
|
|
718
|
-
validator: ({ restored, initial }) => {
|
|
719
|
-
if (typeof restored === 'object' && restored !== null) {
|
|
720
|
-
return { ...initial, ...restored };
|
|
721
|
-
}
|
|
722
|
-
return initial;
|
|
723
|
-
},
|
|
781
|
+
validator: ({ restored, initial }) => prefs.parse({ ...initial, ...(restored as object) }),
|
|
724
782
|
},
|
|
725
783
|
},
|
|
726
784
|
);
|
|
@@ -734,18 +792,22 @@ const useUserPrefs = createGlobalState(
|
|
|
734
792
|
4. After migration, `validator` runs (if provided)
|
|
735
793
|
5. New version is saved to localStorage
|
|
736
794
|
|
|
737
|
-
###
|
|
795
|
+
### Selective Persistence (Optional)
|
|
738
796
|
|
|
739
797
|
Only persist specific parts of your state:
|
|
740
798
|
|
|
741
799
|
```tsx
|
|
742
800
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
801
|
+
import { z } from 'zod';
|
|
743
802
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}
|
|
803
|
+
// The whole state gets a schema, even the part that is never saved
|
|
804
|
+
const appState = z.object({
|
|
805
|
+
user: z.object({ name: z.string(), email: z.string() }),
|
|
806
|
+
settings: z.object({ theme: z.string() }),
|
|
807
|
+
sessionData: z.object({ temp: z.string() }), // Don't persist this!
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
type AppState = z.infer<typeof appState>;
|
|
749
811
|
|
|
750
812
|
const initialAppState: AppState = {
|
|
751
813
|
user: { name: 'Guest', email: '' },
|
|
@@ -765,24 +827,18 @@ const useAppState = createGlobalState(
|
|
|
765
827
|
settings: state.settings,
|
|
766
828
|
}),
|
|
767
829
|
|
|
768
|
-
//
|
|
769
|
-
validator: ({ restored, initial }) => {
|
|
770
|
-
if (typeof restored === 'object' && restored !== null) {
|
|
771
|
-
// Merge persisted data with initial (to get sessionData)
|
|
772
|
-
return { ...initial, ...restored };
|
|
773
|
-
}
|
|
774
|
-
return initial;
|
|
775
|
-
},
|
|
830
|
+
// Storage only carries user and settings, so validate the state they produce
|
|
831
|
+
validator: ({ restored, initial }) => appState.parse({ ...initial, ...(restored as Partial<AppState>) }),
|
|
776
832
|
},
|
|
777
833
|
},
|
|
778
834
|
);
|
|
779
835
|
```
|
|
780
836
|
|
|
781
|
-
###
|
|
837
|
+
### Scoped State with `createContext`
|
|
782
838
|
|
|
783
|
-
|
|
839
|
+
Use `createContext` to give each provider its own store instance.
|
|
784
840
|
|
|
785
|
-
####
|
|
841
|
+
#### The Basics
|
|
786
842
|
|
|
787
843
|
```tsx
|
|
788
844
|
import { createContext } from 'react-global-state-hooks';
|
|
@@ -824,7 +880,7 @@ function FormPreview() {
|
|
|
824
880
|
}
|
|
825
881
|
```
|
|
826
882
|
|
|
827
|
-
####
|
|
883
|
+
#### Provider Variations
|
|
828
884
|
|
|
829
885
|
```tsx
|
|
830
886
|
const ThemeContext = createContext('light');
|
|
@@ -851,7 +907,7 @@ function App() {
|
|
|
851
907
|
}
|
|
852
908
|
```
|
|
853
909
|
|
|
854
|
-
####
|
|
910
|
+
#### Context selectors
|
|
855
911
|
|
|
856
912
|
Use the same selectors, actions and metadata with state scoped to each provider.
|
|
857
913
|
|
|
@@ -880,7 +936,7 @@ function EmailField() {
|
|
|
880
936
|
}
|
|
881
937
|
```
|
|
882
938
|
|
|
883
|
-
####
|
|
939
|
+
#### Context with Actions
|
|
884
940
|
|
|
885
941
|
```tsx
|
|
886
942
|
const CounterContext = createContext(0, {
|
|
@@ -923,9 +979,9 @@ function App() {
|
|
|
923
979
|
}
|
|
924
980
|
```
|
|
925
981
|
|
|
926
|
-
####
|
|
982
|
+
#### Reusable Context Selectors
|
|
927
983
|
|
|
928
|
-
|
|
984
|
+
Compose reusable selectors for scoped stores with the same API.
|
|
929
985
|
|
|
930
986
|
```tsx
|
|
931
987
|
const DataContext = createContext({
|
|
@@ -960,9 +1016,9 @@ function UserList() {
|
|
|
960
1016
|
}
|
|
961
1017
|
```
|
|
962
1018
|
|
|
963
|
-
####
|
|
1019
|
+
#### Testing context stores
|
|
964
1020
|
|
|
965
|
-
|
|
1021
|
+
Access the context store directly to inspect changes, inject test data, and exercise updates.
|
|
966
1022
|
|
|
967
1023
|
```tsx
|
|
968
1024
|
import { renderHook } from '@testing-library/react';
|
|
@@ -988,9 +1044,9 @@ describe('CounterContext', () => {
|
|
|
988
1044
|
});
|
|
989
1045
|
```
|
|
990
1046
|
|
|
991
|
-
####
|
|
1047
|
+
#### Lifecycle Hooks
|
|
992
1048
|
|
|
993
|
-
|
|
1049
|
+
Connect initialization and cleanup to the context lifecycle.
|
|
994
1050
|
|
|
995
1051
|
```tsx
|
|
996
1052
|
const DataContext = createContext([], {
|
|
@@ -1030,11 +1086,11 @@ function App() {
|
|
|
1030
1086
|
|
|
1031
1087
|
---
|
|
1032
1088
|
|
|
1033
|
-
###
|
|
1089
|
+
### External Actions with `actions`
|
|
1034
1090
|
|
|
1035
|
-
|
|
1091
|
+
Define actions in focused modules and bind them to an existing store.
|
|
1036
1092
|
|
|
1037
|
-
####
|
|
1093
|
+
#### Direct Binding
|
|
1038
1094
|
|
|
1039
1095
|
```tsx
|
|
1040
1096
|
import { createGlobalState, actions } from 'react-global-state-hooks';
|
|
@@ -1068,9 +1124,9 @@ counterActions.double(); // count = 10
|
|
|
1068
1124
|
counterActions.decrement(3); // count = 7
|
|
1069
1125
|
```
|
|
1070
1126
|
|
|
1071
|
-
####
|
|
1127
|
+
#### Action templates
|
|
1072
1128
|
|
|
1073
|
-
|
|
1129
|
+
Define action templates before the store API is available, then bind them in a context or lifecycle callback.
|
|
1074
1130
|
|
|
1075
1131
|
```tsx
|
|
1076
1132
|
import { actions, InferAPI, createContext } from 'react-global-state-hooks';
|
|
@@ -1121,9 +1177,9 @@ const { loadData } = SessionContext.use.actions();
|
|
|
1121
1177
|
console.log(loadData); // undefined
|
|
1122
1178
|
```
|
|
1123
1179
|
|
|
1124
|
-
####
|
|
1180
|
+
#### Actions Calling Actions
|
|
1125
1181
|
|
|
1126
|
-
|
|
1182
|
+
Compose actions into larger workflows.
|
|
1127
1183
|
|
|
1128
1184
|
```tsx
|
|
1129
1185
|
const useStore = createGlobalState({ count: 0, history: [] }, {
|
|
@@ -1170,9 +1226,9 @@ const storeActions = actions(useStore, {
|
|
|
1170
1226
|
storeActions.incrementTwice(5);
|
|
1171
1227
|
```
|
|
1172
1228
|
|
|
1173
|
-
####
|
|
1229
|
+
#### Access Store Actions
|
|
1174
1230
|
|
|
1175
|
-
External actions can call each other with `this` and access the store's public actions
|
|
1231
|
+
External actions can call each other with `this` and access the store's public actions.
|
|
1176
1232
|
|
|
1177
1233
|
```tsx
|
|
1178
1234
|
const useStore = createGlobalState(
|
|
@@ -1211,7 +1267,7 @@ const extraActions = actions(useStore, {
|
|
|
1211
1267
|
extraActions.incrementAndLog(5);
|
|
1212
1268
|
```
|
|
1213
1269
|
|
|
1214
|
-
####
|
|
1270
|
+
#### Actions in context stores
|
|
1215
1271
|
|
|
1216
1272
|
```tsx
|
|
1217
1273
|
import { actions, InferAPI } from 'react-global-state-hooks';
|
|
@@ -1260,11 +1316,11 @@ function Counter() {
|
|
|
1260
1316
|
|
|
1261
1317
|
---
|
|
1262
1318
|
|
|
1263
|
-
##
|
|
1319
|
+
## Advanced Patterns
|
|
1264
1320
|
|
|
1265
|
-
###
|
|
1321
|
+
### Production Architecture (File Organization)
|
|
1266
1322
|
|
|
1267
|
-
|
|
1323
|
+
Keep each domain easy to navigate with dedicated modules for its store, actions, selector hooks, and observables:
|
|
1268
1324
|
|
|
1269
1325
|
**File Structure:**
|
|
1270
1326
|
|
|
@@ -1450,9 +1506,9 @@ function TodoApp() {
|
|
|
1450
1506
|
- โ
**Namespace pattern** - Everything bundled: `todos$.useActiveTodos()`, `todos$.activeTodos$`
|
|
1451
1507
|
- โ
**Scalable** - Easy to find, test, and maintain individual pieces
|
|
1452
1508
|
|
|
1453
|
-
###
|
|
1509
|
+
### Smart Subscriptions
|
|
1454
1510
|
|
|
1455
|
-
Subscribe to
|
|
1511
|
+
Subscribe to the state slice your integration needs.
|
|
1456
1512
|
|
|
1457
1513
|
```tsx
|
|
1458
1514
|
const useStore = createGlobalState({
|
|
@@ -1486,11 +1542,11 @@ unsubRole();
|
|
|
1486
1542
|
|
|
1487
1543
|
---
|
|
1488
1544
|
|
|
1489
|
-
##
|
|
1545
|
+
## `uniqueId` - Type-Safe Unique IDs
|
|
1490
1546
|
|
|
1491
|
-
Generate
|
|
1547
|
+
Generate unique identifiers with branded types that distinguish one domain from another.
|
|
1492
1548
|
|
|
1493
|
-
###
|
|
1549
|
+
### Basic Usage
|
|
1494
1550
|
|
|
1495
1551
|
```tsx
|
|
1496
1552
|
import { uniqueId } from 'react-global-state-hooks';
|
|
@@ -1501,9 +1557,9 @@ const id2 = uniqueId('user:'); // "user:a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
|
1501
1557
|
const id3 = uniqueId('session:'); // "session:a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
1502
1558
|
```
|
|
1503
1559
|
|
|
1504
|
-
###
|
|
1560
|
+
### Branded identifiers
|
|
1505
1561
|
|
|
1506
|
-
Create ID generators with compile-time type checking
|
|
1562
|
+
Create ID generators with compile-time type checking.
|
|
1507
1563
|
|
|
1508
1564
|
```tsx
|
|
1509
1565
|
// Create branded generators
|
|
@@ -1520,7 +1576,7 @@ const postId: PostId = generatePostId(); // โ
"post:a1b2c3d4-e5f6-7890-abcd-ef
|
|
|
1520
1576
|
const wrong: UserId = generatePostId(); // โ Type error!
|
|
1521
1577
|
```
|
|
1522
1578
|
|
|
1523
|
-
###
|
|
1579
|
+
### Runtime Validation
|
|
1524
1580
|
|
|
1525
1581
|
```tsx
|
|
1526
1582
|
const generateUserId = uniqueId.for('user:');
|
|
@@ -1540,9 +1596,9 @@ generateUserId.assert('user:a1b2c3d4-e5f6-7890-abcd-ef1234567890'); // โ
OK
|
|
|
1540
1596
|
generateUserId.assert('post:a1b2c3d4-e5f6-7890-abcd-ef1234567890'); // โ Throws error!
|
|
1541
1597
|
```
|
|
1542
1598
|
|
|
1543
|
-
###
|
|
1599
|
+
### Strict Branding
|
|
1544
1600
|
|
|
1545
|
-
|
|
1601
|
+
Use symbol branding to distinguish identifier types even when their prefixes match.
|
|
1546
1602
|
|
|
1547
1603
|
```tsx
|
|
1548
1604
|
declare const UserBrand: unique symbol;
|
|
@@ -1561,7 +1617,7 @@ const id1: UserId = generateUserId(); // โ
|
|
|
1561
1617
|
const id2: UserId = generateUserId2(); // โ Different brands!
|
|
1562
1618
|
```
|
|
1563
1619
|
|
|
1564
|
-
###
|
|
1620
|
+
### Real-World Example
|
|
1565
1621
|
|
|
1566
1622
|
```tsx
|
|
1567
1623
|
import { createGlobalState, uniqueId } from 'react-global-state-hooks';
|
|
@@ -1643,44 +1699,50 @@ actions.addTodo('Build feature', userId);
|
|
|
1643
1699
|
|
|
1644
1700
|
---
|
|
1645
1701
|
|
|
1646
|
-
##
|
|
1702
|
+
## Documentation and examples
|
|
1703
|
+
|
|
1704
|
+
The [React Global State Hooks website](https://johnny-quesada-developer.github.io/react-global-state-hooks/) is the home for current documentation,
|
|
1705
|
+
interactive examples, and the DevTools walkthrough.
|
|
1647
1706
|
|
|
1648
|
-
|
|
|
1649
|
-
|
|
|
1650
|
-
|
|
|
1651
|
-
|
|
|
1652
|
-
|
|
|
1653
|
-
|
|
|
1707
|
+
| Explore | What you will find |
|
|
1708
|
+
| --- | --- |
|
|
1709
|
+
| [Getting started](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/getting-started/) | Create your first store and connect it to your UI. |
|
|
1710
|
+
| [API and guides](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/) | Selectors, actions, scoped state, persistence, and TypeScript. |
|
|
1711
|
+
| [Interactive examples](https://johnny-quesada-developer.github.io/react-global-state-hooks/examples/) | Explore the state model in working browser examples. |
|
|
1712
|
+
| [DevTools](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/devtools/) | Inspect state, follow actions, and use the terminal workflow. |
|
|
1713
|
+
| [Agentic DevTools](https://johnny-quesada-developer.github.io/react-global-state-hooks/agentic-devtools/) | Inspect state, run an action and read back the result from a terminal or a coding agent. |
|
|
1714
|
+
| [Platform guide](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/platform-and-versions/) | Choose the package and persistence model for your app. |
|
|
1654
1715
|
|
|
1655
1716
|
---
|
|
1656
1717
|
|
|
1657
|
-
##
|
|
1718
|
+
## Choose your package
|
|
1658
1719
|
|
|
1659
|
-
|
|
1660
|
-
| -------------------------------------------------------------------------------------------------- | -------------------- | ------------------------ |
|
|
1661
|
-
| [`react-hooks-global-states`](https://www.npmjs.com/package/react-hooks-global-states) | React / React Native | Core library |
|
|
1662
|
-
| [`react-global-state-hooks`](https://www.npmjs.com/package/react-global-state-hooks) | Web | localStorage integration |
|
|
1663
|
-
| [`react-native-global-state-hooks`](https://www.npmjs.com/package/react-native-global-state-hooks) | React Native | AsyncStorage integration |
|
|
1720
|
+
The family shares a common state API, with persistence tailored to each platform.
|
|
1664
1721
|
|
|
1665
|
-
|
|
1722
|
+
| Package | Best fit |
|
|
1723
|
+
| --- | --- |
|
|
1724
|
+
| [`react-global-state-hooks`](https://www.npmjs.com/package/react-global-state-hooks) | React web applications with optional localStorage persistence. |
|
|
1725
|
+
| [`react-native-global-state-hooks`](https://www.npmjs.com/package/react-native-global-state-hooks) | React Native applications with optional asynchronous persistence. |
|
|
1726
|
+
| [`react-hooks-global-states`](https://www.npmjs.com/package/react-hooks-global-states) | The shared core without platform-specific persistence. |
|
|
1727
|
+
| [`react-hooks-global-states-debug`](https://www.npmjs.com/package/react-hooks-global-states-debug) | Development instrumentation for the DevTools extension. |
|
|
1666
1728
|
|
|
1667
|
-
|
|
1729
|
+
See the [platform guide](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/platform-and-versions/) for package differences
|
|
1730
|
+
and the [DevTools guide](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/devtools/) for debug entry points.
|
|
1668
1731
|
|
|
1669
|
-
|
|
1732
|
+
---
|
|
1733
|
+
|
|
1734
|
+
## Built to grow with your application
|
|
1670
1735
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
| โ
Global + Context | โ Either/or choice |
|
|
1678
|
-
| โ
Actions (optional) | โ Required structure |
|
|
1679
|
-
| โ
Familiar hook syntax | โ New component APIs |
|
|
1736
|
+
- **Familiar from the first hook.** Share state through a `useState`-style API.
|
|
1737
|
+
- **Focused subscriptions.** Select the values a component needs.
|
|
1738
|
+
- **Composable structure.** Bring in actions, selectors, and scoped stores as your features grow.
|
|
1739
|
+
- **Persistence where you need it.** Keep selected state in `localStorage` across browser sessions.
|
|
1740
|
+
- **A connected debugging workflow.** Follow state and actions with the [DevTools integration](https://johnny-quesada-developer.github.io/react-global-state-hooks/docs/devtools/).
|
|
1741
|
+
- **Runtime evidence for coding agents.** Let an agent inspect, act and verify against the running app with [Agentic DevTools](https://johnny-quesada-developer.github.io/react-global-state-hooks/agentic-devtools/).
|
|
1680
1742
|
|
|
1681
1743
|
---
|
|
1682
1744
|
|
|
1683
|
-
##
|
|
1745
|
+
## Get Started Now
|
|
1684
1746
|
|
|
1685
1747
|
```bash
|
|
1686
1748
|
npm install react-global-state-hooks
|
|
@@ -1699,14 +1761,23 @@ function App() {
|
|
|
1699
1761
|
}
|
|
1700
1762
|
```
|
|
1701
1763
|
|
|
1702
|
-
|
|
1764
|
+
Continue with the [guides and interactive examples](https://johnny-quesada-developer.github.io/react-global-state-hooks/) to build your next store.
|
|
1703
1765
|
|
|
1704
1766
|
---
|
|
1705
1767
|
|
|
1706
|
-
|
|
1768
|
+
## Built by Johnny Quesada
|
|
1707
1769
|
|
|
1708
|
-
|
|
1770
|
+
Explore the [project website](https://johnny-quesada-developer.github.io/react-global-state-hooks/), meet [Johnny](https://johnny-quesada-developer.github.io/react-global-state-hooks/about/),
|
|
1771
|
+
and help shape what comes next. If the library helps your work, a GitHub star or a
|
|
1772
|
+
shared example helps more developers discover it.
|
|
1709
1773
|
|
|
1710
|
-
|
|
1774
|
+
[Star on GitHub](https://github.com/johnny-quesada-developer/react-global-state-hooks) ยท [Report an issue](https://github.com/johnny-quesada-developer/react-global-state-hooks/issues) ยท [Explore the source](https://github.com/johnny-quesada-developer/react-global-state-hooks/tree/master/libs/web)
|
|
1711
1775
|
|
|
1712
|
-
|
|
1776
|
+
## Legacy resources
|
|
1777
|
+
|
|
1778
|
+
Earlier demos and walkthroughs are preserved here for reference. For current APIs,
|
|
1779
|
+
examples, and setup instructions, start with the [documentation website](https://johnny-quesada-developer.github.io/react-global-state-hooks/).
|
|
1780
|
+
|
|
1781
|
+
- [Original browser demo](https://johnny-quesada-developer.github.io/global-hooks-example/)
|
|
1782
|
+
- [Original video walkthrough](https://www.youtube.com/watch?v=1UBqXk2MH8I)
|
|
1783
|
+
- [Original CodePen example](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-global-state-hooks",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.6",
|
|
4
4
|
"description": "Shared React state with a useState-style API, selective subscriptions, actions, localStorage persistence and DevTools.",
|
|
5
5
|
"main": "./bundle.cjs",
|
|
6
6
|
"module": "./bundle.mjs",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"homepage": "https://github.com/johnny-quesada-developer/react-global-state-hooks#readme",
|
|
104
104
|
"dependencies": {
|
|
105
105
|
"json-storage-formatter": "^4.0.0",
|
|
106
|
-
"react-hooks-global-states": "^16.0.
|
|
106
|
+
"react-hooks-global-states": "^16.0.6"
|
|
107
107
|
},
|
|
108
108
|
"peerDependencies": {
|
|
109
109
|
"json-storage-formatter": "^4.0.0",
|