react-global-state-hooks 16.0.4 → 16.0.5
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/GlobalStore.cjs +1 -1
- package/GlobalStore.js +1 -1
- package/README.md +91 -129
- package/debug.cjs +1 -0
- package/debug.d.ts +2 -0
- package/debug.js +1 -0
- package/debug.mjs +1 -0
- package/package.json +11 -3
- package/rgsh.mjs +2 -0
- package/types.d.ts +2 -2
package/GlobalStore.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var F=Object.create;var g=Object.defineProperty,V=Object.defineProperties,w=Object.getOwnPropertyDescriptor,
|
|
1
|
+
"use strict";var F=Object.create;var g=Object.defineProperty,V=Object.defineProperties,w=Object.getOwnPropertyDescriptor,G=Object.getOwnPropertyDescriptors,W=Object.getOwnPropertyNames,A=Object.getOwnPropertySymbols,B=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable;var v=(o,a,t)=>a in o?g(o,a,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[a]=t,u=(o,a)=>{for(var t in a||(a={}))C.call(a,t)&&v(o,t,a[t]);if(A)for(var t of A(a))L.call(a,t)&&v(o,t,a[t]);return o},k=(o,a)=>V(o,G(a));var R=(o,a)=>{for(var t in a)g(o,t,{get:a[t],enumerable:!0})},E=(o,a,t,e)=>{if(a&&typeof a=="object"||typeof a=="function")for(let r of W(a))!C.call(o,r)&&r!==t&&g(o,r,{get:()=>a[r],enumerable:!(e=w(a,r))||e.enumerable});return o};var l=(o,a,t)=>(t=o!=null?F(B(o)):{},E(a||!o||!o.__esModule?g(t,"default",{value:o,enumerable:!0}):t,o)),$=o=>E(g({},"__esModule",{value:!0}),o);var N={};R(N,{GlobalStore:()=>h,default:()=>z});module.exports=$(N);var M=l(require("react-hooks-global-states/GlobalStore")),c=l(require("./tryCatch.cjs")),T=l(require("json-storage-formatter/formatFromStore")),P=l(require("json-storage-formatter/formatToStore")),f=l(require("json-storage-formatter/isNil")),x=l(require("json-storage-formatter/isPrimitive"));var I=-1,h=class o extends M.default{constructor(t,e={metadata:{}}){var i;super(t,e);this.localStorage=null;this.onInit=()=>{let t=this.localStorage;if(!t||!this.isPersistStorageAvailable())return;let e=t.versioning,{result:r,error:i}=(0,c.default)(()=>{var p,y,b;if((p=t.adapter)!=null&&p.getItem)return{s:(y=t.adapter)==null?void 0:y.getItem(t.key),v:(b=e==null?void 0:e.version)!=null?b:I};let n=this.getStorageItem();return n&&t.selector&&!(0,x.default)(n==null?void 0:n.s)?k(u({},n),{s:u(u({},this.getState()),n.s)}):n});if(i){this.handleStorageError(i),this.updateStateWithValidation(this.getState());return}if(!r){this.updateStateWithValidation(this.getState());return}let s=r.v===(e==null?void 0:e.version),S=e==null?void 0:e.migrator;if(s||!S||t.adapter){this.updateStateWithValidation(r.s);return}let{result:d,error:m}=(0,c.default)(()=>S({legacy:r.s,initial:this.getState()},this.storeTools));if(!m){this.updateStateWithValidation(d);return}this.handleStorageError(m),this.updateStateWithValidation(this.getState())};this.onStateChanged=({state:t})=>{let e=this.localStorage;if(!e||!this.isPersistStorageAvailable())return;let{error:r}=(0,c.default)(()=>{var s;let i=(s=e.adapter)==null?void 0:s.setItem;if(i)return i(e.key,t);this.setStorageItem(t)});r&&this.handleStorageError(r)};this.localStorage=(i=e.localStorage)!=null?i:null,this.constructor===o&&this.initialize()}isPersistStorageAvailable(){var t;return!!((t=this.localStorage)!=null&&t.key&&(globalThis!=null&&globalThis.localStorage))}trySetStorageItem(t){let e=this.localStorage;if(!e)return;let{error:r}=(0,c.default)(()=>{var s;let i=(s=e.adapter)==null?void 0:s.setItem;if(!i)return this.setStorageItem(t);i(e.key,t)});r&&this.handleStorageError(r)}updateStateWithValidation(t){let e=this.localStorage;if(!e)return;let{result:r,error:i}=(0,c.default)(()=>{var s;return(s=e.validator)==null?void 0:s.call(e,{restored:t,initial:this.getState()},this.storeTools)});if(i){this.handleStorageError(i),this.trySetStorageItem(this.getState());return}if(r===void 0){if(t===void 0){this.trySetStorageItem(this.getState());return}this.setState(t),this.trySetStorageItem(t);return}this.setState(r),this.trySetStorageItem(r)}getStorageItem(){let t=globalThis.localStorage.getItem(this.localStorage.key),e=(0,f.default)(t)?null:(0,T.default)(t);return j(this.localStorage.key,e),e}setStorageItem(t){var i,s,S,d;let e={s:(i=this.localStorage)!=null&&i.selector?this.localStorage.selector(t):t,v:(d=(S=(s=this.localStorage)==null?void 0:s.versioning)==null?void 0:S.version)!=null?d:I},r=(0,P.default)(e);globalThis.localStorage.setItem(this.localStorage.key,r)}reset(...t){this.isPersistStorageAvailable()&&t.length===0&&globalThis.localStorage.removeItem(this.localStorage.key),super.reset(...t)}handleStorageError(t){var e,r;if((e=this.localStorage)!=null&&e.onError)return this.localStorage.onError(t,this.storeTools);globalThis.console.error([`[react-global-state-hooks]
|
|
2
2
|
`," localStorage sync error:","[hook]:",` ${this._name}`,"[Localstorage Key]:",` ${(r=this.localStorage)==null?void 0:r.key}`,"[Error]:",` ${t!=null?t:"undefined"}
|
|
3
3
|
|
|
4
4
|
`,"Stacktrace:"].join(`
|
package/GlobalStore.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var F=Object.create;var g=Object.defineProperty,V=Object.defineProperties,w=Object.getOwnPropertyDescriptor,
|
|
1
|
+
"use strict";var F=Object.create;var g=Object.defineProperty,V=Object.defineProperties,w=Object.getOwnPropertyDescriptor,G=Object.getOwnPropertyDescriptors,W=Object.getOwnPropertyNames,A=Object.getOwnPropertySymbols,B=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable;var v=(o,a,t)=>a in o?g(o,a,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[a]=t,u=(o,a)=>{for(var t in a||(a={}))C.call(a,t)&&v(o,t,a[t]);if(A)for(var t of A(a))L.call(a,t)&&v(o,t,a[t]);return o},k=(o,a)=>V(o,G(a));var R=(o,a)=>{for(var t in a)g(o,t,{get:a[t],enumerable:!0})},E=(o,a,t,e)=>{if(a&&typeof a=="object"||typeof a=="function")for(let r of W(a))!C.call(o,r)&&r!==t&&g(o,r,{get:()=>a[r],enumerable:!(e=w(a,r))||e.enumerable});return o};var l=(o,a,t)=>(t=o!=null?F(B(o)):{},E(a||!o||!o.__esModule?g(t,"default",{value:o,enumerable:!0}):t,o)),$=o=>E(g({},"__esModule",{value:!0}),o);var N={};R(N,{GlobalStore:()=>h,default:()=>z});module.exports=$(N);var M=l(require("react-hooks-global-states/GlobalStore")),c=l(require("./tryCatch.js")),T=l(require("json-storage-formatter/formatFromStore")),P=l(require("json-storage-formatter/formatToStore")),f=l(require("json-storage-formatter/isNil")),x=l(require("json-storage-formatter/isPrimitive"));var I=-1,h=class o extends M.default{constructor(t,e={metadata:{}}){var i;super(t,e);this.localStorage=null;this.onInit=()=>{let t=this.localStorage;if(!t||!this.isPersistStorageAvailable())return;let e=t.versioning,{result:r,error:i}=(0,c.default)(()=>{var p,y,b;if((p=t.adapter)!=null&&p.getItem)return{s:(y=t.adapter)==null?void 0:y.getItem(t.key),v:(b=e==null?void 0:e.version)!=null?b:I};let n=this.getStorageItem();return n&&t.selector&&!(0,x.default)(n==null?void 0:n.s)?k(u({},n),{s:u(u({},this.getState()),n.s)}):n});if(i){this.handleStorageError(i),this.updateStateWithValidation(this.getState());return}if(!r){this.updateStateWithValidation(this.getState());return}let s=r.v===(e==null?void 0:e.version),S=e==null?void 0:e.migrator;if(s||!S||t.adapter){this.updateStateWithValidation(r.s);return}let{result:d,error:m}=(0,c.default)(()=>S({legacy:r.s,initial:this.getState()},this.storeTools));if(!m){this.updateStateWithValidation(d);return}this.handleStorageError(m),this.updateStateWithValidation(this.getState())};this.onStateChanged=({state:t})=>{let e=this.localStorage;if(!e||!this.isPersistStorageAvailable())return;let{error:r}=(0,c.default)(()=>{var s;let i=(s=e.adapter)==null?void 0:s.setItem;if(i)return i(e.key,t);this.setStorageItem(t)});r&&this.handleStorageError(r)};this.localStorage=(i=e.localStorage)!=null?i:null,this.constructor===o&&this.initialize()}isPersistStorageAvailable(){var t;return!!((t=this.localStorage)!=null&&t.key&&(globalThis!=null&&globalThis.localStorage))}trySetStorageItem(t){let e=this.localStorage;if(!e)return;let{error:r}=(0,c.default)(()=>{var s;let i=(s=e.adapter)==null?void 0:s.setItem;if(!i)return this.setStorageItem(t);i(e.key,t)});r&&this.handleStorageError(r)}updateStateWithValidation(t){let e=this.localStorage;if(!e)return;let{result:r,error:i}=(0,c.default)(()=>{var s;return(s=e.validator)==null?void 0:s.call(e,{restored:t,initial:this.getState()},this.storeTools)});if(i){this.handleStorageError(i),this.trySetStorageItem(this.getState());return}if(r===void 0){if(t===void 0){this.trySetStorageItem(this.getState());return}this.setState(t),this.trySetStorageItem(t);return}this.setState(r),this.trySetStorageItem(r)}getStorageItem(){let t=globalThis.localStorage.getItem(this.localStorage.key),e=(0,f.default)(t)?null:(0,T.default)(t);return j(this.localStorage.key,e),e}setStorageItem(t){var i,s,S,d;let e={s:(i=this.localStorage)!=null&&i.selector?this.localStorage.selector(t):t,v:(d=(S=(s=this.localStorage)==null?void 0:s.versioning)==null?void 0:S.version)!=null?d:I},r=(0,P.default)(e);globalThis.localStorage.setItem(this.localStorage.key,r)}reset(...t){this.isPersistStorageAvailable()&&t.length===0&&globalThis.localStorage.removeItem(this.localStorage.key),super.reset(...t)}handleStorageError(t){var e,r;if((e=this.localStorage)!=null&&e.onError)return this.localStorage.onError(t,this.storeTools);globalThis.console.error([`[react-global-state-hooks]
|
|
2
2
|
`," localStorage sync error:","[hook]:",` ${this._name}`,"[Localstorage Key]:",` ${(r=this.localStorage)==null?void 0:r.key}`,"[Error]:",` ${t!=null?t:"undefined"}
|
|
3
3
|
|
|
4
4
|
`,"Stacktrace:"].join(`
|
package/README.md
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**Shared React state. Precise subscriptions. Built-in DevTools.** 🚀
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
_The familiarity of `useState`, with the power of a shared store._
|
|
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
|
-
[](https://github.com/johnny-quesada-developer/react-global-state-hooks/blob/
|
|
17
|
+
[](https://github.com/johnny-quesada-developer/react-global-state-hooks/blob/master/LICENSE)
|
|
18
18
|
|
|
19
|
-
[**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) • [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I
|
|
19
|
+
[**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) • [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I) • [**CodePen**](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010)
|
|
20
20
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
@@ -30,7 +30,7 @@ import { createGlobalState } from 'react-global-state-hooks';
|
|
|
30
30
|
export const useCounter = createGlobalState(0);
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
**That's it.** No providers. No context boilerplate. No configuration files.
|
|
33
|
+
**That's it.** No providers. No context boilerplate. No configuration files. Shared state with a familiar React API.
|
|
34
34
|
|
|
35
35
|
```tsx
|
|
36
36
|
// Use it anywhere, instantly
|
|
@@ -42,9 +42,9 @@ function Counter() {
|
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
|
-
## 🚀
|
|
45
|
+
## 🚀 Built for React applications
|
|
46
46
|
|
|
47
|
-
### 🎓 **
|
|
47
|
+
### 🎓 **Familiar React API**
|
|
48
48
|
|
|
49
49
|
```tsx
|
|
50
50
|
// If you know this...
|
|
@@ -54,9 +54,9 @@ const [state, setState] = useState(0);
|
|
|
54
54
|
const [state, setState] = useGlobalState();
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
### ⚡ **
|
|
57
|
+
### ⚡ **Precise subscriptions**
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Subscribe to a slice so unrelated store changes do not trigger component updates.
|
|
60
60
|
|
|
61
61
|
```tsx
|
|
62
62
|
// Only re-renders when name changes
|
|
@@ -66,7 +66,7 @@ const [name] = useStore((s) => s.user.name);
|
|
|
66
66
|
### 🔗 **Chainable Selectors**
|
|
67
67
|
|
|
68
68
|
```tsx
|
|
69
|
-
const useUsers =
|
|
69
|
+
const useUsers = useStore.createSelectorHook((s) => s.users);
|
|
70
70
|
const useAdmins = useUsers.createSelectorHook((users) => users.filter((u) => u.isAdmin));
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -142,64 +142,53 @@ function ThemedComponent() {
|
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
###
|
|
145
|
+
### Async state with actions
|
|
146
|
+
|
|
147
|
+
Keep loading and error values in state so components update throughout the request.
|
|
146
148
|
|
|
147
149
|
```tsx
|
|
148
150
|
import { createGlobalState } from 'react-global-state-hooks';
|
|
149
151
|
|
|
152
|
+
type User = { id: string; name: string };
|
|
153
|
+
|
|
150
154
|
const useAuth = createGlobalState(
|
|
151
|
-
{ user: null,
|
|
155
|
+
() => ({ user: null as User | null, isLoading: false, error: null as string | null }),
|
|
152
156
|
{
|
|
153
|
-
// Non-reactive metadata
|
|
154
|
-
metadata: { isLoading: false },
|
|
155
|
-
|
|
156
|
-
// Persist to localStorage automatically!
|
|
157
|
-
localStorage: {
|
|
158
|
-
key: 'auth-token',
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
// Type-safe actions
|
|
162
157
|
actions: {
|
|
163
|
-
login(
|
|
164
|
-
return async ({ setState
|
|
165
|
-
|
|
158
|
+
login(authenticate: () => Promise<User>) {
|
|
159
|
+
return async ({ setState }) => {
|
|
160
|
+
setState((state) => ({ ...state, isLoading: true, error: null }));
|
|
166
161
|
try {
|
|
167
|
-
const
|
|
168
|
-
setState({ user,
|
|
169
|
-
return { success: true };
|
|
162
|
+
const user = await authenticate();
|
|
163
|
+
setState({ user, isLoading: false, error: null });
|
|
170
164
|
} catch (error) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
setState((state) => ({
|
|
166
|
+
...state,
|
|
167
|
+
isLoading: false,
|
|
168
|
+
error: error instanceof Error ? error.message : 'Sign-in failed',
|
|
169
|
+
}));
|
|
174
170
|
}
|
|
175
171
|
};
|
|
176
172
|
},
|
|
177
|
-
|
|
178
173
|
logout() {
|
|
179
174
|
return ({ setState }) => {
|
|
180
|
-
setState({ user: null,
|
|
175
|
+
setState({ user: null, isLoading: false, error: null });
|
|
181
176
|
};
|
|
182
177
|
},
|
|
183
178
|
},
|
|
184
179
|
},
|
|
185
180
|
);
|
|
186
181
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const [auth, actions] = useAuth();
|
|
190
|
-
const { isLoading } = useAuth.getMetadata();
|
|
191
|
-
|
|
192
|
-
const handleSubmit = async (e) => {
|
|
193
|
-
e.preventDefault();
|
|
194
|
-
const result = await actions.login(email, password);
|
|
195
|
-
if (!result.success) toast.error(result.error);
|
|
196
|
-
};
|
|
182
|
+
function LoginButton({ authenticate }: { authenticate: () => Promise<User> }) {
|
|
183
|
+
const [{ isLoading, error }, actions] = useAuth();
|
|
197
184
|
|
|
198
185
|
return (
|
|
199
|
-
<
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
186
|
+
<div>
|
|
187
|
+
<button disabled={isLoading} onClick={() => void actions.login(authenticate)}>
|
|
188
|
+
{isLoading ? 'Signing in…' : 'Sign in'}
|
|
189
|
+
</button>
|
|
190
|
+
{error && <p role="alert">{error}</p>}
|
|
191
|
+
</div>
|
|
203
192
|
);
|
|
204
193
|
}
|
|
205
194
|
```
|
|
@@ -237,7 +226,7 @@ const useExpensiveState = createGlobalState(() => {
|
|
|
237
226
|
|
|
238
227
|
#### 🎯 Surgical Re-renders with Selectors
|
|
239
228
|
|
|
240
|
-
|
|
229
|
+
Selectors keep components subscribed to the state they use.
|
|
241
230
|
|
|
242
231
|
```tsx
|
|
243
232
|
const useStore = createGlobalState({
|
|
@@ -277,16 +266,16 @@ function NotificationCount() {
|
|
|
277
266
|
}
|
|
278
267
|
```
|
|
279
268
|
|
|
280
|
-
**
|
|
269
|
+
**Subscription behavior:**
|
|
281
270
|
|
|
282
|
-
|
|
|
283
|
-
|
|
|
284
|
-
|
|
|
285
|
-
|
|
|
271
|
+
| Subscription | What triggers an update |
|
|
272
|
+
| --- | --- |
|
|
273
|
+
| `useStore()` | Any store value change |
|
|
274
|
+
| `useStore(selector)` | A change to the selected value |
|
|
286
275
|
|
|
287
276
|
#### ⚡ Computed Values with Dependencies
|
|
288
277
|
|
|
289
|
-
Derive values
|
|
278
|
+
Derive values from both store state and component-local values, with explicit dependencies.
|
|
290
279
|
|
|
291
280
|
```tsx
|
|
292
281
|
const useStore = createGlobalState({
|
|
@@ -362,9 +351,9 @@ function AdvancedFiltered() {
|
|
|
362
351
|
}
|
|
363
352
|
```
|
|
364
353
|
|
|
365
|
-
#### 🔗 Reusable Selector Hooks
|
|
354
|
+
#### 🔗 Reusable Selector Hooks
|
|
366
355
|
|
|
367
|
-
|
|
356
|
+
Compose reusable selector hooks. Each hook returns its selected value directly.
|
|
368
357
|
|
|
369
358
|
```tsx
|
|
370
359
|
const useStore = createGlobalState({
|
|
@@ -392,9 +381,9 @@ const useCurrentUser = useStore.createSelectorHook((state) => {
|
|
|
392
381
|
|
|
393
382
|
// Now use them anywhere!
|
|
394
383
|
function UserStats() {
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
const
|
|
384
|
+
const totalUsers = useUsers();
|
|
385
|
+
const activeUsers = useActiveUsers();
|
|
386
|
+
const activeAdmins = useActiveAdmins();
|
|
398
387
|
|
|
399
388
|
return (
|
|
400
389
|
<div>
|
|
@@ -406,19 +395,24 @@ function UserStats() {
|
|
|
406
395
|
}
|
|
407
396
|
|
|
408
397
|
function CurrentUserProfile() {
|
|
409
|
-
const
|
|
398
|
+
const user = useCurrentUser();
|
|
410
399
|
return <div>{user?.name}</div>;
|
|
411
400
|
}
|
|
412
401
|
|
|
413
|
-
//
|
|
414
|
-
function
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
402
|
+
// Selector hooks return values. Update the source store through its API.
|
|
403
|
+
function ActivateAll() {
|
|
404
|
+
return (
|
|
405
|
+
<button
|
|
406
|
+
onClick={() =>
|
|
407
|
+
useStore.setState((state) => ({
|
|
408
|
+
...state,
|
|
409
|
+
users: state.users.map((user) => ({ ...user, active: true })),
|
|
410
|
+
}))
|
|
411
|
+
}
|
|
412
|
+
>
|
|
413
|
+
Activate all users
|
|
414
|
+
</button>
|
|
415
|
+
);
|
|
422
416
|
}
|
|
423
417
|
```
|
|
424
418
|
|
|
@@ -575,7 +569,7 @@ const unsubscribe = useAuth.subscribe(
|
|
|
575
569
|
);
|
|
576
570
|
```
|
|
577
571
|
|
|
578
|
-
#### 🔭 Observable Fragments
|
|
572
|
+
#### 🔭 Observable Fragments
|
|
579
573
|
|
|
580
574
|
Create observable slices of your state for reactive programming!
|
|
581
575
|
|
|
@@ -605,46 +599,23 @@ const doubledObservable = countObservable.createObservable((count) => count * 2)
|
|
|
605
599
|
|
|
606
600
|
#### 📋 Metadata - Non-Reactive Side Info
|
|
607
601
|
|
|
608
|
-
|
|
602
|
+
Keep request counters and timestamps in metadata when they are read only by handlers or background work.
|
|
603
|
+
Put loading indicators and displayed errors in state so components subscribe to their changes.
|
|
609
604
|
|
|
610
605
|
```tsx
|
|
611
606
|
const useStore = createGlobalState(
|
|
612
|
-
{ items: [] },
|
|
613
|
-
{
|
|
614
|
-
metadata: {
|
|
615
|
-
isLoading: false,
|
|
616
|
-
lastFetch: null,
|
|
617
|
-
error: null,
|
|
618
|
-
retryCount: 0,
|
|
619
|
-
},
|
|
620
|
-
},
|
|
607
|
+
{ items: [] as string[] },
|
|
608
|
+
{ metadata: { requests: 0, lastFetchedAt: null as number | null } },
|
|
621
609
|
);
|
|
622
610
|
|
|
623
|
-
// Metadata
|
|
624
|
-
useStore.setMetadata({
|
|
625
|
-
|
|
626
|
-
// But you can access it anytime
|
|
627
|
-
const meta = useStore.getMetadata();
|
|
628
|
-
console.log(meta.isLoading); // true
|
|
611
|
+
// Metadata is replaced, so preserve the fields you are not updating.
|
|
612
|
+
useStore.setMetadata((metadata) => ({ ...metadata, requests: metadata.requests + 1 }));
|
|
629
613
|
|
|
630
|
-
//
|
|
631
|
-
async function fetchData() {
|
|
632
|
-
useStore.setMetadata({ isLoading: true, error: null });
|
|
614
|
+
console.log(useStore.metadata.requests); // 1
|
|
633
615
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
useStore.setMetadata({
|
|
638
|
-
isLoading: false,
|
|
639
|
-
lastFetch: new Date(),
|
|
640
|
-
});
|
|
641
|
-
} catch (error) {
|
|
642
|
-
useStore.setMetadata({
|
|
643
|
-
isLoading: false,
|
|
644
|
-
error: error.message,
|
|
645
|
-
retryCount: useStore.getMetadata().retryCount + 1,
|
|
646
|
-
});
|
|
647
|
-
}
|
|
616
|
+
function recordFetchedItems(items: string[]) {
|
|
617
|
+
useStore.setMetadata((metadata) => ({ ...metadata, lastFetchedAt: Date.now() }));
|
|
618
|
+
useStore.setState({ items });
|
|
648
619
|
}
|
|
649
620
|
```
|
|
650
621
|
|
|
@@ -776,12 +747,14 @@ type AppState = {
|
|
|
776
747
|
sessionData: { temp: string }; // Don't persist this!
|
|
777
748
|
};
|
|
778
749
|
|
|
779
|
-
const
|
|
780
|
-
{
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
750
|
+
const initialAppState: AppState = {
|
|
751
|
+
user: { name: 'Guest', email: '' },
|
|
752
|
+
settings: { theme: 'light' },
|
|
753
|
+
sessionData: { temp: 'session-123' },
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
const useAppState = createGlobalState(
|
|
757
|
+
initialAppState,
|
|
785
758
|
{
|
|
786
759
|
localStorage: {
|
|
787
760
|
key: 'app-state',
|
|
@@ -880,7 +853,7 @@ function App() {
|
|
|
880
853
|
|
|
881
854
|
#### 🎯 Context + Selectors = ❤️
|
|
882
855
|
|
|
883
|
-
|
|
856
|
+
Use the same selectors, actions and metadata with state scoped to each provider.
|
|
884
857
|
|
|
885
858
|
```tsx
|
|
886
859
|
const FormContext = createContext({
|
|
@@ -976,7 +949,7 @@ function App() {
|
|
|
976
949
|
}
|
|
977
950
|
|
|
978
951
|
function UserList() {
|
|
979
|
-
const
|
|
952
|
+
const activeUsers = useActiveUsers();
|
|
980
953
|
return (
|
|
981
954
|
<ul>
|
|
982
955
|
{activeUsers.map((u) => (
|
|
@@ -1143,7 +1116,7 @@ const SessionContext = createContext(
|
|
|
1143
1116
|
},
|
|
1144
1117
|
);
|
|
1145
1118
|
|
|
1146
|
-
// Internal actions are not
|
|
1119
|
+
// Internal actions are not exposed through the context
|
|
1147
1120
|
const { loadData } = SessionContext.use.actions();
|
|
1148
1121
|
console.log(loadData); // undefined
|
|
1149
1122
|
```
|
|
@@ -1469,9 +1442,9 @@ function TodoApp() {
|
|
|
1469
1442
|
}
|
|
1470
1443
|
```
|
|
1471
1444
|
|
|
1472
|
-
**
|
|
1445
|
+
**Benefits of this structure:**
|
|
1473
1446
|
|
|
1474
|
-
- ✅ **
|
|
1447
|
+
- ✅ **Focused modules** - Keep related state, actions and selectors easy to navigate
|
|
1475
1448
|
- ✅ **Type-safe** - Everything is strongly typed
|
|
1476
1449
|
- ✅ **Public/Private APIs** - Internal actions don't pollute public interface
|
|
1477
1450
|
- ✅ **Namespace pattern** - Everything bundled: `todos$.useActiveTodos()`, `todos$.activeTodos$`
|
|
@@ -1675,7 +1648,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1675
1648
|
| Resource | Description |
|
|
1676
1649
|
| ------------------------------------------------------------------------------------ | --------------------------------- |
|
|
1677
1650
|
| 🎮 [**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) | Interactive examples |
|
|
1678
|
-
| 🎥 [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I
|
|
1651
|
+
| 🎥 [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I) | Full walkthrough |
|
|
1679
1652
|
| 💻 [**CodePen**](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010) | Try it online |
|
|
1680
1653
|
| 📚 **400+ Tests** | Check the test suite for patterns |
|
|
1681
1654
|
|
|
@@ -1685,7 +1658,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1685
1658
|
|
|
1686
1659
|
| Package | Platform | Special Feature |
|
|
1687
1660
|
| -------------------------------------------------------------------------------------------------- | -------------------- | ------------------------ |
|
|
1688
|
-
| [`react-global-
|
|
1661
|
+
| [`react-hooks-global-states`](https://www.npmjs.com/package/react-hooks-global-states) | React / React Native | Core library |
|
|
1689
1662
|
| [`react-global-state-hooks`](https://www.npmjs.com/package/react-global-state-hooks) | Web | localStorage integration |
|
|
1690
1663
|
| [`react-native-global-state-hooks`](https://www.npmjs.com/package/react-native-global-state-hooks) | React Native | AsyncStorage integration |
|
|
1691
1664
|
|
|
@@ -1693,18 +1666,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1693
1666
|
|
|
1694
1667
|
## 🎉 Why Developers Choose This
|
|
1695
1668
|
|
|
1696
|
-
|
|
1697
|
-
"I replaced 500 lines of Redux with 50 lines of this. Mind blown." 🤯
|
|
1698
|
-
- Every developer who tries it
|
|
1699
|
-
|
|
1700
|
-
"The useState I always wanted." ❤️
|
|
1701
|
-
- React developers everywhere
|
|
1702
|
-
|
|
1703
|
-
"Finally, state management that doesn't fight me." 🥊
|
|
1704
|
-
- Tired developers worldwide
|
|
1705
|
-
```
|
|
1706
|
-
|
|
1707
|
-
### The Bottom Line
|
|
1669
|
+
### At a glance
|
|
1708
1670
|
|
|
1709
1671
|
| What You Get | What You Don't |
|
|
1710
1672
|
| --------------------------- | --------------------- |
|
|
@@ -1714,7 +1676,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1714
1676
|
| ✅ TypeScript inference | ❌ Manual typing |
|
|
1715
1677
|
| ✅ Global + Context | ❌ Either/or choice |
|
|
1716
1678
|
| ✅ Actions (optional) | ❌ Required structure |
|
|
1717
|
-
| ✅
|
|
1679
|
+
| ✅ Familiar hook syntax | ❌ New component APIs |
|
|
1718
1680
|
|
|
1719
1681
|
---
|
|
1720
1682
|
|
package/debug.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var a=Object.prototype.hasOwnProperty;var b=(p,o,i,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of x(o))!a.call(p,r)&&r!==i&&e(p,r,{get:()=>o[r],enumerable:!(t=m(o,r))||t.enumerable});return p};var c=p=>b(e({},"__esModule",{value:!0}),p);var d={};module.exports=c(d);var g=require("react-hooks-global-states/debug");
|
package/debug.d.ts
ADDED
package/debug.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var a=Object.prototype.hasOwnProperty;var b=(p,o,i,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of x(o))!a.call(p,r)&&r!==i&&e(p,r,{get:()=>o[r],enumerable:!(t=m(o,r))||t.enumerable});return p};var c=p=>b(e({},"__esModule",{value:!0}),p);var d={};module.exports=c(d);var g=require("react-hooks-global-states/debug");
|
package/debug.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"react-hooks-global-states/debug";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-global-state-hooks",
|
|
3
|
-
"version": "16.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "16.0.5",
|
|
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",
|
|
7
7
|
"types": "./index.d.ts",
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"import": "./actions.mjs",
|
|
58
58
|
"require": "./actions.cjs"
|
|
59
59
|
},
|
|
60
|
+
"./debug": {
|
|
61
|
+
"types": "./debug.d.ts",
|
|
62
|
+
"import": "./debug.mjs",
|
|
63
|
+
"require": "./debug.cjs"
|
|
64
|
+
},
|
|
60
65
|
"./package.json": "./package.json"
|
|
61
66
|
},
|
|
62
67
|
"files": [
|
|
@@ -98,7 +103,7 @@
|
|
|
98
103
|
"homepage": "https://github.com/johnny-quesada-developer/react-global-state-hooks#readme",
|
|
99
104
|
"dependencies": {
|
|
100
105
|
"json-storage-formatter": "^4.0.0",
|
|
101
|
-
"react-hooks-global-states": "^16.0.
|
|
106
|
+
"react-hooks-global-states": "^16.0.5"
|
|
102
107
|
},
|
|
103
108
|
"peerDependencies": {
|
|
104
109
|
"json-storage-formatter": "^4.0.0",
|
|
@@ -111,5 +116,8 @@
|
|
|
111
116
|
"json-storage-formatter": {
|
|
112
117
|
"optional": false
|
|
113
118
|
}
|
|
119
|
+
},
|
|
120
|
+
"bin": {
|
|
121
|
+
"rgsh": "./rgsh.mjs"
|
|
114
122
|
}
|
|
115
123
|
}
|
package/rgsh.mjs
ADDED
package/types.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ export interface CreateGlobalState {
|
|
|
128
128
|
* @param args.name optional name for debugging purposes
|
|
129
129
|
* @param args.metadata optional non-reactive metadata associated with the state
|
|
130
130
|
* @param args.callbacks optional lifecycle callbacks for the global state
|
|
131
|
-
* @param args.actions
|
|
131
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API
|
|
132
132
|
* @param args.localStorage optional configuration to persist the state in local storage
|
|
133
133
|
* @returns a state hook that you can use in your components
|
|
134
134
|
*
|
|
@@ -185,7 +185,7 @@ export interface CreateGlobalState {
|
|
|
185
185
|
* @param args.name optional name for debugging purposes
|
|
186
186
|
* @param args.metadata optional non-reactive metadata associated with the state
|
|
187
187
|
* @param args.callbacks optional lifecycle callbacks for the global state
|
|
188
|
-
* @param args.actions
|
|
188
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API
|
|
189
189
|
* @param args.localStorage optional configuration to persist the state in local storage
|
|
190
190
|
* @returns a state hook that you can use in your components
|
|
191
191
|
*
|