react-global-state-hooks 14.0.2 → 14.1.0-beta.1
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.d.ts +11 -6
- package/GlobalStore.js +1 -1
- package/README.md +137 -148
- package/bundle.js +1 -1
- package/createGlobalState.d.ts +3 -3
- package/index.d.ts +0 -2
- package/package.json +38 -25
- package/tryCatch.d.ts +9 -0
- package/types.d.ts +84 -6
- package/webpack.config.js +1 -5
- package/getLocalStorageItem.d.ts +0 -3
- package/getLocalStorageItem.js +0 -1
- package/setLocalStorageItem.d.ts +0 -3
- package/setLocalStorageItem.js +0 -1
package/GlobalStore.d.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import type { ActionCollectionConfig, ActionCollectionResult, AnyFunction, BaseMetadata, GlobalStoreCallbacks, StateChanges, StoreTools } from 'react-hooks-global-states/types';
|
|
2
2
|
import { GlobalStoreAbstract } from 'react-hooks-global-states/GlobalStoreAbstract';
|
|
3
|
-
import { LocalStorageConfig } from './types';
|
|
3
|
+
import type { LocalStorageConfig } from './types';
|
|
4
4
|
export declare class GlobalStore<State, Metadata extends BaseMetadata, ActionsConfig extends ActionCollectionConfig<State, Metadata> | undefined | unknown, PublicStateMutator = keyof ActionsConfig extends never | undefined ? React.Dispatch<React.SetStateAction<State>> : ActionCollectionResult<State, Metadata, NonNullable<ActionsConfig>>> extends GlobalStoreAbstract<State, Metadata, ActionsConfig> {
|
|
5
|
-
protected localStorage: LocalStorageConfig | null;
|
|
5
|
+
protected localStorage: LocalStorageConfig<State> | null;
|
|
6
6
|
constructor(state: State);
|
|
7
7
|
constructor(state: State, args: {
|
|
8
8
|
metadata?: Metadata;
|
|
9
9
|
callbacks?: GlobalStoreCallbacks<State, PublicStateMutator extends AnyFunction ? null : PublicStateMutator, Metadata>;
|
|
10
10
|
actions?: ActionsConfig;
|
|
11
11
|
name?: string;
|
|
12
|
-
localStorage?: LocalStorageConfig
|
|
12
|
+
localStorage?: LocalStorageConfig<State>;
|
|
13
13
|
});
|
|
14
|
-
protected
|
|
15
|
-
protected _onInitialize: ({
|
|
16
|
-
|
|
14
|
+
protected isGlobalLocalStorageAvailable: () => boolean;
|
|
15
|
+
protected _onInitialize: ({ getState }: StoreTools<State, PublicStateMutator, Metadata>) => void;
|
|
16
|
+
private trySetLocalStorageItem;
|
|
17
|
+
private updateStateWithValidation;
|
|
18
|
+
protected _onChange: ({ state, }: StoreTools<State, PublicStateMutator, Metadata> & StateChanges<State>) => void;
|
|
17
19
|
/**
|
|
18
20
|
* We set it to null so the instances of the GlobalStoreAbstract can override it.
|
|
19
21
|
*/
|
|
@@ -25,5 +27,8 @@ export declare class GlobalStore<State, Metadata extends BaseMetadata, ActionsCo
|
|
|
25
27
|
*/
|
|
26
28
|
protected onInit: (parameters: StoreTools<State, PublicStateMutator, Metadata>) => void;
|
|
27
29
|
protected onStateChanged: (args: StoreTools<State, PublicStateMutator, Metadata> & StateChanges<State>) => void;
|
|
30
|
+
private getLocalStorageItem;
|
|
31
|
+
private setLocalStorageItem;
|
|
32
|
+
private handleLocalStorageError;
|
|
28
33
|
}
|
|
29
34
|
export default GlobalStore;
|
package/GlobalStore.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var t,
|
|
1
|
+
var t,o;t=this,o=(t,o,e,r)=>(()=>{"use strict";var a={70:(t,o)=>{Object.defineProperty(o,"__esModule",{value:!0}),o.default=function(t){try{return{result:t(),error:null}}catch(t){return{result:null,error:t}}}},266:o=>{o.exports=t},330:t=>{t.exports=o},413:t=>{t.exports=e},773:t=>{t.exports=r}},l={};function n(t){var o=l[t];if(void 0!==o)return o.exports;var e=l[t]={exports:{}};return a[t](e,e.exports,n),e.exports}var i={};return(()=>{var t=i;function o(t){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o(t)}function e(t,e){if(e&&("object"==o(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}function r(){try{var t=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(t){}return(r=function(){return!!t})()}function a(t){return a=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},a(t)}function l(t,o){return l=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,o){return t.__proto__=o,t},l(t,o)}var u=function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(t,"__esModule",{value:!0}),t.GlobalStore=void 0;var c=n(266),s=u(n(70)),f=u(n(330)),d=u(n(413)),g=u(n(773)),v=function(t){function o(t){var l,n,i,u,c,v=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{metadata:{}};return function(t,o){if(!(t instanceof o))throw new TypeError("Cannot call a class as a function")}(this,o),i=this,c=[t,v],u=a(u=o),(l=e(i,r()?Reflect.construct(u,c||[],a(i).constructor):u.apply(i,c))).localStorage=null,l.isGlobalLocalStorageAvailable=function(){var t;return Boolean((null===(t=l.localStorage)||void 0===t?void 0:t.key)&&(null===globalThis||void 0===globalThis?void 0:globalThis.localStorage))},l._onInitialize=function(t){var o=t.getState,e=l.localStorage;if(e&&l.isGlobalLocalStorageAvailable()){var r=e.versioning,a=(0,s.default)(function(){var t,o,a=null===(t=e.adapter)||void 0===t?void 0:t.getItem;return a?{s:a(e.key),v:null!==(o=null==r?void 0:r.version)&&void 0!==o?o:-1}:l.getLocalStorageItem()}),n=a.result,i=a.error;if(i)return l.handleLocalStorageError(i),void l.updateStateWithValidation(l.getState());if(n){var u=n.v===(null==r?void 0:r.version),c=null==r?void 0:r.migrator;if(u||!c||e.adapter)l.updateStateWithValidation(n.s);else{var f=(0,s.default)(function(){return c({legacy:n.s,initial:l.getState()})}),d=f.result,g=f.error;g?(l.handleLocalStorageError(g),l.updateStateWithValidation(l.getState())):l.updateStateWithValidation(d)}}else l.updateStateWithValidation(o())}},l.trySetLocalStorageItem=function(t){var o=l.localStorage;if(o){var e=(0,s.default)(function(){var e,r=null===(e=o.adapter)||void 0===e?void 0:e.setItem;if(!r)return l.setLocalStorageItem(t);r(o.key,t)}),r=e.error;r&&l.handleLocalStorageError(r)}},l.updateStateWithValidation=function(t){var o=l.localStorage;if(o){var e=(0,s.default)(function(){return o.validator({restored:t,initial:l.getState()})}),r=e.result,a=e.error;if(a)return l.handleLocalStorageError(a),void l.trySetLocalStorageItem(l.getState());l.setState(r),l.trySetLocalStorageItem(r)}},l._onChange=function(t){var o=t.state,e=l.localStorage;if(e&&l.isGlobalLocalStorageAvailable()){var r=(0,s.default)(function(){var t,r=null===(t=e.adapter)||void 0===t?void 0:t.setItem;if(r)return r(e.key,o);l.setLocalStorageItem(o)}),a=r.error;a&&l.handleLocalStorageError(a)}},l.onInitialize=null,l.onChange=null,l.onInit=function(t){var o;null===(o=l._onInitialize)||void 0===o||o.call(l,t)},l.onStateChanged=function(t){var o;null===(o=l._onChange)||void 0===o||o.call(l,t)},l.getLocalStorageItem=function(){var t=globalThis.localStorage.getItem(l.localStorage.key);return(0,g.default)(t)?null:(0,f.default)(t)},l.setLocalStorageItem=function(t){var o,e,r,a={s:t,v:null!==(r=null===(e=null===(o=l.localStorage)||void 0===o?void 0:o.versioning)||void 0===e?void 0:e.version)&&void 0!==r?r:-1},n=(0,d.default)(a);globalThis.localStorage.setItem(l.localStorage.key,n)},l.handleLocalStorageError=function(t){var o;if(null===(o=l.localStorage)||void 0===o?void 0:o.onError)return l.localStorage.onError(t)},l.localStorage=null!==(n=v.localStorage)&&void 0!==n?n:null,l.constructor!==o?e(l):(l.initialize(),l)}return function(t,o){if("function"!=typeof o&&null!==o)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(o&&o.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),o&&l(t,o)}(o,t),n=o,Object.defineProperty(n,"prototype",{writable:!1}),n;var n}(c.GlobalStoreAbstract);t.GlobalStore=v,t.default=v})(),i})(),"object"==typeof exports&&"object"==typeof module?module.exports=o(require("react-hooks-global-states/GlobalStoreAbstract"),require("json-storage-formatter/formatFromStore"),require("json-storage-formatter/formatToStore"),require("json-storage-formatter/isNil")):"function"==typeof define&&define.amd?define(["react-hooks-global-states/GlobalStoreAbstract","json-storage-formatter/formatFromStore","json-storage-formatter/formatToStore","json-storage-formatter/isNil"],o):"object"==typeof exports?exports["react-global-state-hooks"]=o(require("react-hooks-global-states/GlobalStoreAbstract"),require("json-storage-formatter/formatFromStore"),require("json-storage-formatter/formatToStore"),require("json-storage-formatter/isNil")):t["react-global-state-hooks"]=o(t["react-hooks-global-states/GlobalStoreAbstract"],t["json-storage-formatter/formatFromStore"],t["json-storage-formatter/formatToStore"],t["json-storage-formatter/isNil"]);
|
package/README.md
CHANGED
|
@@ -13,67 +13,36 @@ Effortless **global state management** for `React` & `React Native` and `Preact`
|
|
|
13
13
|
|
|
14
14
|
Works seamlessly with **React & React Native**:
|
|
15
15
|
|
|
16
|
-
- **[react-hooks-global-states](https://www.npmjs.com/package/react-hooks-global-states)** compatible with both `React & React Native`
|
|
17
16
|
- **[react-global-state-hooks](https://www.npmjs.com/package/react-global-state-hooks)** specific for web applications (**local-storage integration**).
|
|
18
17
|
- **[react-native-global-state-hooks](https://www.npmjs.com/package/react-native-global-state-hooks)** specific for React Native projects (**async-storage integration**).
|
|
19
18
|
|
|
20
19
|
---
|
|
21
20
|
|
|
22
|
-
## 🚀 React Hooks Global States - DevTools Extension
|
|
23
|
-
|
|
24
|
-
React Hooks Global States includes a dedicated, `devTools extension` to streamline your development workflow! Easily visualize, inspect, debug, and modify your application's global state in real-time right within your browser.
|
|
25
|
-
|
|
26
|
-
### 🔗 [Install the DevTools Extension for Chrome](https://chromewebstore.google.com/detail/bafojplmkpejhglhjpibpdhoblickpee/preview?hl=en&authuser=0)
|
|
27
|
-
|
|
28
|
-
### 📸 DevTools Highlights
|
|
29
|
-
|
|
30
|
-
| **Track State Changes** | **Modify the State** |
|
|
31
|
-
| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
-
|  |  |
|
|
33
|
-
| Effortlessly monitor state updates and history. | Instantly edit global states directly from the extension. |
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
| **Restore the State** | **Custom Actions Granularity** |
|
|
38
|
-
| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
-
|  |  |
|
|
40
|
-
| Quickly revert your application to a previous state. | Precisely debug specific actions affecting state changes. |
|
|
41
|
-
|
|
42
|
-
<br>
|
|
43
|
-
|
|
44
|
-
## 🗂️ Persisting State with LocalStorage
|
|
45
|
-
|
|
46
|
-
To persist the global state using **LocalStorage**, simply add the `localStorage` option:
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
const useContacts = createGlobalState(new Map(), {
|
|
50
|
-
localStorage: {
|
|
51
|
-
key: 'contacts',
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
21
|
## 🛠 Creating a Global State
|
|
57
22
|
|
|
58
|
-
Define a **global state** in **one line**:
|
|
59
|
-
|
|
60
23
|
```tsx
|
|
61
|
-
import
|
|
24
|
+
import createGlobalState from 'react-hooks-global-states/createGlobalState';
|
|
25
|
+
|
|
62
26
|
export const useCount = createGlobalState(0);
|
|
63
27
|
```
|
|
64
28
|
|
|
65
|
-
|
|
29
|
+
`useCount` will work as a regular `useState` hook, but the state will persist across your entire app!
|
|
66
30
|
|
|
67
31
|
```tsx
|
|
32
|
+
// Component A
|
|
68
33
|
const [count, setCount] = useCount();
|
|
34
|
+
|
|
69
35
|
return <Button onClick={() => setCount((count) => count + 1)}>{count}</Button>;
|
|
70
|
-
```
|
|
71
36
|
|
|
72
|
-
|
|
37
|
+
// Component B
|
|
38
|
+
const [count] = useCount();
|
|
39
|
+
|
|
40
|
+
return <Text>Count is: {count}</Text>;
|
|
41
|
+
```
|
|
73
42
|
|
|
74
43
|
---
|
|
75
44
|
|
|
76
|
-
## 🎯 Selectors
|
|
45
|
+
## 🎯 Selectors
|
|
77
46
|
|
|
78
47
|
For **complex state objects**, you can subscribe to specific properties instead of the entire state:
|
|
79
48
|
|
|
@@ -84,7 +53,9 @@ export const useContacts = createGlobalState({ entities: [], selected: new Set<n
|
|
|
84
53
|
To access only the `entities` property:
|
|
85
54
|
|
|
86
55
|
```tsx
|
|
87
|
-
const [
|
|
56
|
+
const [filter, setFilter] = useContacts((state) => state.filter);
|
|
57
|
+
const contacts = useContacts.select((state) => state.entities, [filter]);
|
|
58
|
+
|
|
88
59
|
return (
|
|
89
60
|
<ul>
|
|
90
61
|
{contacts.map((contact) => (
|
|
@@ -94,34 +65,20 @@ return (
|
|
|
94
65
|
);
|
|
95
66
|
```
|
|
96
67
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
You can also add **dependencies** to a selector. This is useful when you want to derive state based on another piece of state (e.g., a filtered list). For example, if you're filtering contacts based on a `filter` value:
|
|
68
|
+
Alternatively, you can also define the `isEqual` and `isEqualRoot` options to optimize re-selection:
|
|
100
69
|
|
|
101
70
|
```tsx
|
|
102
|
-
const
|
|
103
|
-
(state) => state.entities.filter((item) => item.name.includes(filter)),
|
|
104
|
-
[filter],
|
|
105
|
-
);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Alternatively, you can pass dependencies inside an **options object**:
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
const [contacts] = useContacts((state) => state.entities.filter((item) => item.name.includes(filter)), {
|
|
71
|
+
const contacts = useContacts.select((state) => state.entities.filter((item) => item.name.includes(filter)), {
|
|
112
72
|
dependencies: [filter],
|
|
73
|
+
isEqual: (a, b) => a.length === b.length,
|
|
113
74
|
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
114
75
|
});
|
|
115
76
|
```
|
|
116
77
|
|
|
117
|
-
Unlike Redux, where only **root state changes trigger re-selection**, this approach ensures that **derived values recompute when dependencies change** while maintaining performance.
|
|
118
|
-
|
|
119
78
|
---
|
|
120
79
|
|
|
121
80
|
## 🔄 Reusing Selectors
|
|
122
81
|
|
|
123
|
-
### 📌 Creating a Selector
|
|
124
|
-
|
|
125
82
|
```tsx
|
|
126
83
|
export const useContactsArray = useContacts.createSelectorHook((state) => state.entities);
|
|
127
84
|
export const useContactsCount = useContactsArray.createSelectorHook((entities) => entities.length);
|
|
@@ -130,30 +87,38 @@ export const useContactsCount = useContactsArray.createSelectorHook((entities) =
|
|
|
130
87
|
### 📌 Using Selectors in Components
|
|
131
88
|
|
|
132
89
|
```tsx
|
|
133
|
-
const
|
|
134
|
-
const
|
|
90
|
+
const contacts = useContactsArray();
|
|
91
|
+
const count = useContactsCount();
|
|
135
92
|
```
|
|
136
93
|
|
|
137
|
-
#### ✅ Selectors
|
|
138
|
-
|
|
139
|
-
You can still **use dependencies** inside a selector hook:
|
|
94
|
+
#### ✅ Inline Selectors
|
|
140
95
|
|
|
141
96
|
```tsx
|
|
142
|
-
const
|
|
97
|
+
const filteredContacts = useContactsArray(
|
|
143
98
|
(contacts) => contacts.filter((c) => c.name.includes(filter)),
|
|
144
99
|
[filter],
|
|
145
100
|
);
|
|
146
101
|
```
|
|
147
102
|
|
|
148
|
-
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🗂️ Persisting State with LocalStorage
|
|
149
106
|
|
|
150
|
-
|
|
107
|
+
To persist the global state using **LocalStorage**, simply add the `localStorage` option:
|
|
151
108
|
|
|
152
109
|
```tsx
|
|
153
|
-
const
|
|
154
|
-
|
|
110
|
+
const useContacts = createGlobalState(new Map(), {
|
|
111
|
+
localStorage: {
|
|
112
|
+
key: 'contacts',
|
|
155
113
|
|
|
156
|
-
|
|
114
|
+
// validator is mandatory to prevent corrupted data from populating the state
|
|
115
|
+
validator: ({ restored, initial }) => {
|
|
116
|
+
// validate the restored value
|
|
117
|
+
if (!isMap(restored)) return initial;
|
|
118
|
+
return restored as typeof initial;
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
157
122
|
```
|
|
158
123
|
|
|
159
124
|
---
|
|
@@ -163,47 +128,78 @@ console.log(actions1 === actions2); // true
|
|
|
163
128
|
Restrict **state modifications** by defining custom actions:
|
|
164
129
|
|
|
165
130
|
```tsx
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
131
|
+
const initialValue = {
|
|
132
|
+
filter: '',
|
|
133
|
+
contacts: [] as Array<{ id: number; name: string; email?: string }>,
|
|
134
|
+
status: 'idle' as 'idle' | 'loading' | 'success' | 'error',
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/** *
|
|
138
|
+
* For complex state, prefer a semantic, declarative store.
|
|
139
|
+
* For example let's add the suffix `$` which is a common convention to denote stores.
|
|
140
|
+
*/
|
|
141
|
+
export const contacts$ = createGlobalState(initialValue, {
|
|
142
|
+
actions: {
|
|
143
|
+
async fetch() {
|
|
144
|
+
return async ({ setState }) => {
|
|
145
|
+
setState((s) => ({ ...s, status: 'loading' }));
|
|
146
|
+
|
|
147
|
+
const contacts = await fetchItems();
|
|
148
|
+
|
|
149
|
+
setState((s) => ({ ...s, contacts, status: 'success' }));
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
setFilter(filter: string) {
|
|
154
|
+
return ({ setState }) => {
|
|
155
|
+
setState((s) => ({ ...s, filter }));
|
|
156
|
+
};
|
|
181
157
|
},
|
|
182
158
|
},
|
|
183
|
-
);
|
|
159
|
+
});
|
|
184
160
|
```
|
|
185
161
|
|
|
186
|
-
|
|
162
|
+
Then inside your components, you can use your global hook as a store:
|
|
187
163
|
|
|
188
164
|
```tsx
|
|
189
|
-
|
|
165
|
+
// Component A
|
|
166
|
+
// Subscribe to changes of the contacts property
|
|
167
|
+
const contacts = contacts$.use.select((s) => s.contacts);
|
|
168
|
+
|
|
169
|
+
// Component B
|
|
170
|
+
const onChangeFilter = (newFilter: string) => {
|
|
171
|
+
// Directly use the actions from the store without subscribing to state changes
|
|
172
|
+
contacts$.actions.setFilter(newFilter);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Other examples
|
|
176
|
+
const useReusableSelector = contacts$.createSelectorHook((s) => s.filter);
|
|
177
|
+
const observable = contacts$.createObservable((s) => `Status is: \${s.status}`);
|
|
178
|
+
const metadata = contacts$.getMetadata(); // non-reactive metadata
|
|
190
179
|
```
|
|
191
180
|
|
|
192
181
|
---
|
|
193
182
|
|
|
194
183
|
## 🌍 Accessing Global State Outside Components
|
|
195
184
|
|
|
196
|
-
|
|
185
|
+
As mentioned earlier, you can use the store actions outside components by directly calling actions and dispatchers from the store.
|
|
197
186
|
|
|
198
187
|
```tsx
|
|
199
|
-
|
|
200
|
-
|
|
188
|
+
/**
|
|
189
|
+
* contacts$.actions has access to all the defined actions of the store
|
|
190
|
+
* @example:
|
|
191
|
+
*/
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
contacts$.actions.fetch();
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
console.log(contacts$.getState()); // Retrieves the current state without subscription
|
|
201
197
|
```
|
|
202
198
|
|
|
203
|
-
#### ✅ Subscribe to changes
|
|
199
|
+
#### ✅ Subscribe to changes outside components
|
|
204
200
|
|
|
205
201
|
```tsx
|
|
206
|
-
const unsubscribe =
|
|
202
|
+
const unsubscribe = contacts$.subscribe((state) => {
|
|
207
203
|
console.log('State updated:', state);
|
|
208
204
|
});
|
|
209
205
|
```
|
|
@@ -211,13 +207,19 @@ const unsubscribe = contactsRetriever((state) => {
|
|
|
211
207
|
#### ✅ Subscriptions are great when one state depends on another.
|
|
212
208
|
|
|
213
209
|
```tsx
|
|
214
|
-
const
|
|
210
|
+
const initialValue = null as string | null;
|
|
211
|
+
|
|
212
|
+
const selectedId$ = createGlobalState(initialValue, {
|
|
215
213
|
callbacks: {
|
|
216
214
|
onInit: ({ setState, getState }) => {
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Let's create a subscription to contacts$ to clear the selectedId if the contact is removed
|
|
217
|
+
*/
|
|
218
|
+
contacts$.subscribe(
|
|
219
|
+
(state) => state.contacts, // listen only to contacts changes
|
|
219
220
|
(contacts) => {
|
|
220
|
-
|
|
221
|
+
const hasContactId = contacts.has(getState());
|
|
222
|
+
if (!hasContactId) setState(null);
|
|
221
223
|
},
|
|
222
224
|
);
|
|
223
225
|
},
|
|
@@ -229,66 +231,26 @@ const useSelectedContact = createGlobalState(null, {
|
|
|
229
231
|
|
|
230
232
|
## 🎭 Using Context for Scoped State
|
|
231
233
|
|
|
232
|
-
- **Scoped State** – Context state is **isolated inside the provider**.
|
|
233
|
-
- **Same API** – Context supports **selectors, actions, and state controls**.
|
|
234
|
-
|
|
235
234
|
### 📌 Creating a Context
|
|
236
235
|
|
|
237
236
|
```tsx
|
|
238
237
|
import { createContext } from 'react-global-state-hooks/createContext';
|
|
239
|
-
|
|
238
|
+
|
|
239
|
+
export const counter$ = createContext(0);
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
Wrap your app:
|
|
243
243
|
|
|
244
244
|
```tsx
|
|
245
|
-
<
|
|
245
|
+
<counter$.Provider>
|
|
246
246
|
<MyComponent />
|
|
247
|
-
</
|
|
247
|
+
</counter$.Provider>
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
Use the context state:
|
|
251
251
|
|
|
252
252
|
```tsx
|
|
253
|
-
const [count] =
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### 📌 Context Selectors
|
|
257
|
-
|
|
258
|
-
Works **just like global state**, but within the provider.
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## 🔥 Observables: Watching State Changes
|
|
263
|
-
|
|
264
|
-
Observables **let you react to state changes** via subscriptions.
|
|
265
|
-
|
|
266
|
-
### 📌 Creating an Observable
|
|
267
|
-
|
|
268
|
-
```tsx
|
|
269
|
-
export const useCounter = createGlobalState(0);
|
|
270
|
-
export const counterLogs = useCounter.createObservable((count) => `Counter is at ${count}`);
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### 📌 Subscribing to an Observable
|
|
274
|
-
|
|
275
|
-
```tsx
|
|
276
|
-
const unsubscribe = counterLogs((message) => {
|
|
277
|
-
console.log(message);
|
|
278
|
-
});
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### 📌 Using Observables Inside Context
|
|
282
|
-
|
|
283
|
-
```tsx
|
|
284
|
-
export const [useStateControls, useObservableBuilder] = useCounterContext.stateControls();
|
|
285
|
-
const createObservable = useObservableBuilder();
|
|
286
|
-
useEffect(() => {
|
|
287
|
-
const unsubscribe = createObservable((count) => {
|
|
288
|
-
console.log(`Updated count: ${count}`);
|
|
289
|
-
});
|
|
290
|
-
return unsubscribe;
|
|
291
|
-
}, []);
|
|
253
|
+
const [count, setCount] = counter$.use();
|
|
292
254
|
```
|
|
293
255
|
|
|
294
256
|
---
|
|
@@ -298,14 +260,15 @@ useEffect(() => {
|
|
|
298
260
|
| Feature | `createGlobalState` | `createContext` |
|
|
299
261
|
| ---------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
300
262
|
| **Scope** | Available globally across the entire app | Scoped to the Provider where it’s used |
|
|
301
|
-
| **How to Use** | `const useCount = createGlobalState(0)` | `const
|
|
302
|
-
| **createSelectorHook** | `useCount.createSelectorHook` | `
|
|
263
|
+
| **How to Use** | `const useCount = createGlobalState(0)` | `const counter$ = createContext(0)` |
|
|
264
|
+
| **createSelectorHook** | `useCount.createSelectorHook` | `counter$.createSelectorHook` |
|
|
303
265
|
| **inline selectors?** | ✅ Supported | ✅ Supported |
|
|
304
266
|
| **Custom Actions** | ✅ Supported | ✅ Supported |
|
|
305
|
-
| **Observables** | `useCount.createObservable` | `
|
|
306
|
-
| **State Controls** | `useCount.stateControls()` | `const [useStateControls] = useCountContext.stateControls()` |
|
|
267
|
+
| **Observables** | `useCount.createObservable` | `counter$ = counter.use.observable()` |
|
|
307
268
|
| **Best For** | Global app state (auth, settings, cache) | Scoped module state, reusable component state, or state shared between child components without being fully global |
|
|
308
269
|
|
|
270
|
+
---
|
|
271
|
+
|
|
309
272
|
## 🔄 Lifecycle Methods
|
|
310
273
|
|
|
311
274
|
Global state hooks support lifecycle callbacks for additional control.
|
|
@@ -331,9 +294,11 @@ const useData = createGlobalState(
|
|
|
331
294
|
|
|
332
295
|
Use **`onInit`** for setup, **`onStateChanged`** to listen to updates, and **`computePreventStateChange`** to prevent unnecessary updates.
|
|
333
296
|
|
|
297
|
+
---
|
|
298
|
+
|
|
334
299
|
## Metadata
|
|
335
300
|
|
|
336
|
-
There is a possibility to add non
|
|
301
|
+
There is a possibility to add non-reactive information in the global state:
|
|
337
302
|
|
|
338
303
|
```tsx
|
|
339
304
|
const useCount = createGlobalState(0, { metadata: { renders: 0 } });
|
|
@@ -347,6 +312,30 @@ const [count, , metadata] = useCount();
|
|
|
347
312
|
metadata.renders += 1;
|
|
348
313
|
```
|
|
349
314
|
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## 🚀 React Hooks Global States - DevTools Extension
|
|
318
|
+
|
|
319
|
+
React Hooks Global States includes a dedicated, `devTools extension` to streamline your development workflow! Easily visualize, inspect, debug, and modify your application's global state in real-time right within your browser.
|
|
320
|
+
|
|
321
|
+
### 🔗 [Install the DevTools Extension for Chrome](https://chromewebstore.google.com/detail/bafojplmkpejhglhjpibpdhoblickpee/preview?hl=en&authuser=0)
|
|
322
|
+
|
|
323
|
+
### 📸 DevTools Highlights
|
|
324
|
+
|
|
325
|
+
| **Track State Changes** | **Modify the State** |
|
|
326
|
+
| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
327
|
+
|  |  |
|
|
328
|
+
| Effortlessly monitor state updates and history. | Instantly edit global states directly from the extension. |
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
| **Restore the State** | **Custom Actions Granularity** |
|
|
333
|
+
| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
334
|
+
|  |  |
|
|
335
|
+
| Quickly revert your application to a previous state. | Precisely debug specific actions affecting state changes. |
|
|
336
|
+
|
|
337
|
+
<br>
|
|
338
|
+
|
|
350
339
|
## 🎯 Ready to Try It?
|
|
351
340
|
|
|
352
341
|
📦 **NPM Package:** [react-hooks-global-states](https://www.npmjs.com/package/react-hooks-global-states)
|
package/bundle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,
|
|
1
|
+
var e,r;e=this,r=(e,r,t,o,n,a,l,i)=>(()=>{"use strict";var s={78:r=>{r.exports=e},361:e=>{e.exports=r},487:e=>{e.exports=t},623:e=>{e.exports=o},670:e=>{e.exports=n},673:e=>{e.exports=a},778:e=>{e.exports=l},804:e=>{e.exports=i}},c={};function u(e){var r=c[e];if(void 0!==r)return r.exports;var t=c[e]={exports:{}};return s[e](t,t.exports,u),t.exports}var b={};return(()=>{var e=b;Object.defineProperty(e,"__esModule",{value:!0}),e.createGlobalState=e.GlobalStoreAbstract=e.GlobalStore=e.createContext=e.isRecord=e.throwWrongKeyOnActionCollectionConfig=e.uniqueId=e.shallowCompare=void 0;var r=u(673);Object.defineProperty(e,"shallowCompare",{enumerable:!0,get:function(){return r.shallowCompare}});var t=u(78);Object.defineProperty(e,"uniqueId",{enumerable:!0,get:function(){return t.uniqueId}});var o=u(361);Object.defineProperty(e,"throwWrongKeyOnActionCollectionConfig",{enumerable:!0,get:function(){return o.throwWrongKeyOnActionCollectionConfig}});var n=u(487);Object.defineProperty(e,"isRecord",{enumerable:!0,get:function(){return n.isRecord}});var a=u(623);Object.defineProperty(e,"createContext",{enumerable:!0,get:function(){return a.createContext}});var l=u(778);Object.defineProperty(e,"GlobalStore",{enumerable:!0,get:function(){return l.GlobalStore}});var i=u(804);Object.defineProperty(e,"GlobalStoreAbstract",{enumerable:!0,get:function(){return i.GlobalStoreAbstract}});var s=u(670);Object.defineProperty(e,"createGlobalState",{enumerable:!0,get:function(){return s.createGlobalState}})})(),b})(),"object"==typeof exports&&"object"==typeof module?module.exports=r(require("./uniqueId.js"),require("./throwWrongKeyOnActionCollectionConfig.js"),require("./isRecord.js"),require("./createContext.js"),require("./createGlobalState.js"),require("./shallowCompare.js"),require("./GlobalStore.js"),require("./GlobalStoreAbstract.js")):"function"==typeof define&&define.amd?define(["./uniqueId.js","./throwWrongKeyOnActionCollectionConfig.js","./isRecord.js","./createContext.js","./createGlobalState.js","./shallowCompare.js","./GlobalStore.js","./GlobalStoreAbstract.js"],r):"object"==typeof exports?exports["react-global-state-hooks"]=r(require("./uniqueId.js"),require("./throwWrongKeyOnActionCollectionConfig.js"),require("./isRecord.js"),require("./createContext.js"),require("./createGlobalState.js"),require("./shallowCompare.js"),require("./GlobalStore.js"),require("./GlobalStoreAbstract.js")):e["react-global-state-hooks"]=r(e["./uniqueId.js"],e["./throwWrongKeyOnActionCollectionConfig.js"],e["./isRecord.js"],e["./createContext.js"],e["./createGlobalState.js"],e["./shallowCompare.js"],e["./GlobalStore.js"],e["./GlobalStoreAbstract.js"]);
|
package/createGlobalState.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { StateHook, BaseMetadata, ActionCollectionConfig, ActionCollectionResult, GlobalStoreCallbacks } from 'react-hooks-global-states/types';
|
|
3
3
|
export type { InferActionsType } from 'react-hooks-global-states/createGlobalState';
|
|
4
|
-
import { LocalStorageConfig } from './types';
|
|
4
|
+
import type { LocalStorageConfig } from './types';
|
|
5
5
|
interface CreateGlobalState {
|
|
6
6
|
/**
|
|
7
7
|
* Creates a global state hook.
|
|
@@ -78,7 +78,7 @@ interface CreateGlobalState {
|
|
|
78
78
|
metadata?: Metadata;
|
|
79
79
|
callbacks?: GlobalStoreCallbacks<State, PublicStateMutator, Metadata>;
|
|
80
80
|
actions?: ActionsConfig;
|
|
81
|
-
localStorage?: LocalStorageConfig
|
|
81
|
+
localStorage?: LocalStorageConfig<State>;
|
|
82
82
|
}): StateHook<State, PublicStateMutator, Metadata>;
|
|
83
83
|
/**
|
|
84
84
|
* Creates a global state hook that you can use across your application
|
|
@@ -135,7 +135,7 @@ interface CreateGlobalState {
|
|
|
135
135
|
metadata?: Metadata;
|
|
136
136
|
callbacks?: GlobalStoreCallbacks<State, PublicStateMutator, Metadata>;
|
|
137
137
|
actions: ActionsConfig;
|
|
138
|
-
localStorage?: LocalStorageConfig
|
|
138
|
+
localStorage?: LocalStorageConfig<State>;
|
|
139
139
|
}): StateHook<State, PublicStateMutator, Metadata>;
|
|
140
140
|
}
|
|
141
141
|
/**
|
package/index.d.ts
CHANGED
|
@@ -12,5 +12,3 @@ export { type LocalStorageConfig } from './types';
|
|
|
12
12
|
export { GlobalStore } from './GlobalStore';
|
|
13
13
|
export { GlobalStoreAbstract } from './GlobalStoreAbstract';
|
|
14
14
|
export { createGlobalState, type InferActionsType } from './createGlobalState';
|
|
15
|
-
export { getLocalStorageItem } from './getLocalStorageItem';
|
|
16
|
-
export { setLocalStorageItem } from './setLocalStorageItem';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-global-state-hooks",
|
|
3
|
-
"version": "14.0.
|
|
4
|
-
"description": "This is a package to easily handling global-state across your react components
|
|
3
|
+
"version": "14.1.0-beta.1",
|
|
4
|
+
"description": "This is a package to easily handling global-state across your react components",
|
|
5
5
|
"main": "./bundle.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -36,16 +36,6 @@
|
|
|
36
36
|
"require": "./types.js",
|
|
37
37
|
"types": "./types.d.ts"
|
|
38
38
|
},
|
|
39
|
-
"./getLocalStorageItem": {
|
|
40
|
-
"import": "./getLocalStorageItem.js",
|
|
41
|
-
"require": "./getLocalStorageItem.js",
|
|
42
|
-
"types": "./getLocalStorageItem.d.ts"
|
|
43
|
-
},
|
|
44
|
-
"./setLocalStorageItem": {
|
|
45
|
-
"import": "./setLocalStorageItem.js",
|
|
46
|
-
"require": "./setLocalStorageItem.js",
|
|
47
|
-
"types": "./setLocalStorageItem.d.ts"
|
|
48
|
-
},
|
|
49
39
|
"./isRecord": {
|
|
50
40
|
"import": "./isRecord.js",
|
|
51
41
|
"require": "./isRecord.js",
|
|
@@ -72,16 +62,20 @@
|
|
|
72
62
|
"*.d.ts"
|
|
73
63
|
],
|
|
74
64
|
"scripts": {
|
|
65
|
+
"format": "prettier --write . --log-level silent && yarn lint:fix",
|
|
66
|
+
"test": "yarn build && yarn jest",
|
|
67
|
+
"test:debug:no-watch": "node --inspect-brk node_modules/.bin/jest --no-watchman --runInBand",
|
|
75
68
|
"test:debug": "node --inspect-brk node_modules/.bin/jest --watch --runInBand",
|
|
76
|
-
"test:quick": "
|
|
77
|
-
"test:coverage": "
|
|
78
|
-
"build": "yarn ts-check &&yarn clean && webpack --config webpack.config.js",
|
|
79
|
-
"prepare": "yarn
|
|
80
|
-
"version": "npm run format && git add -A
|
|
69
|
+
"test:quick": "yarn test --no-coverage --maxWorkers=4 -c --no-watchman -u",
|
|
70
|
+
"test:coverage": "yarn test --maxWorkers=4 -c --colors --no-watchman --verbose --coverage",
|
|
71
|
+
"build": "yarn ts-check && yarn clean && webpack --config webpack.config.js",
|
|
72
|
+
"prepare": "yarn format && npm run build && yarn test:quick",
|
|
73
|
+
"version": "npm run format && yarn build && yarn jest && git add -A",
|
|
81
74
|
"postversion": "git push && git push --tags",
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
75
|
+
"lint": "eslint . --max-warnings=0",
|
|
76
|
+
"lint:fix": "eslint . --fix --max-warnings=0",
|
|
77
|
+
"clean": "find . -maxdepth 1 -type f \\( -name '*.js' -o -name '*.d.ts' \\) ! -name 'webpack.config.js' ! -name 'eslint.config.mts' -exec rm {} + && rm -rf coverage",
|
|
78
|
+
"ts-check": "tsc -p tsconfig.json --noEmit"
|
|
85
79
|
},
|
|
86
80
|
"repository": {
|
|
87
81
|
"type": "git",
|
|
@@ -122,18 +116,27 @@
|
|
|
122
116
|
"@babel/preset-env": "^7.20.2",
|
|
123
117
|
"@babel/preset-react": "^7.18.6",
|
|
124
118
|
"@babel/preset-typescript": "^7.21.0",
|
|
119
|
+
"@eslint/js": "^9.39.1",
|
|
125
120
|
"@testing-library/dom": "^10.4.0",
|
|
126
121
|
"@testing-library/react": "^16.3.0",
|
|
127
122
|
"@types/jest": "^29.5.11",
|
|
128
123
|
"@types/minimatch": "^6.0.0",
|
|
129
124
|
"@types/react": "^18.2.45",
|
|
130
125
|
"@types/react-dom": "^18.2.18",
|
|
126
|
+
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
|
127
|
+
"@typescript-eslint/parser": "^4.9.1",
|
|
131
128
|
"babel-loader": "^9.1.2",
|
|
132
129
|
"clean-webpack-plugin": "^4.0.0",
|
|
133
130
|
"easy-cancelable-promise": "^1.0.1",
|
|
131
|
+
"eslint": "^9.39.1",
|
|
132
|
+
"eslint-config-airbnb": "^18.2.1",
|
|
133
|
+
"eslint-plugin-import": "^2.22.1",
|
|
134
|
+
"eslint-plugin-jsx-a11y": "^6.4.1",
|
|
135
|
+
"eslint-plugin-react": "^7.37.5",
|
|
136
|
+
"eslint-plugin-react-hooks": "^4.2.0",
|
|
134
137
|
"jest": "^29.7.0",
|
|
135
138
|
"jest-environment-jsdom": "^30.0.4",
|
|
136
|
-
"
|
|
139
|
+
"jiti": "^2.6.1",
|
|
137
140
|
"prettier": "^3.6.2",
|
|
138
141
|
"react": "^18.2.0",
|
|
139
142
|
"react-dom": "^18.2.0",
|
|
@@ -141,14 +144,24 @@
|
|
|
141
144
|
"ts-loader": "^9.5.1",
|
|
142
145
|
"tslib": "^2.6.2",
|
|
143
146
|
"typescript": "^5.8.2",
|
|
147
|
+
"typescript-eslint": "^8.46.3",
|
|
144
148
|
"webpack": "^5.76.3",
|
|
145
149
|
"webpack-cli": "^5.0.1"
|
|
146
150
|
},
|
|
151
|
+
"dependencies": {
|
|
152
|
+
"react-hooks-global-states": "14.1.0-beta.2",
|
|
153
|
+
"json-storage-formatter": "^3.0.2"
|
|
154
|
+
},
|
|
147
155
|
"peerDependencies": {
|
|
148
|
-
"
|
|
149
|
-
"
|
|
156
|
+
"react": ">=18.0.0",
|
|
157
|
+
"json-storage-formatter": "^3.0.2"
|
|
150
158
|
},
|
|
151
|
-
"
|
|
152
|
-
"react
|
|
159
|
+
"peerDependenciesMeta": {
|
|
160
|
+
"react": {
|
|
161
|
+
"optional": false
|
|
162
|
+
},
|
|
163
|
+
"json-storage-formatter": {
|
|
164
|
+
"optional": false
|
|
165
|
+
}
|
|
153
166
|
}
|
|
154
167
|
}
|
package/tryCatch.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type TryCatchResult<T extends (...args: any[]) => any> = {
|
|
2
|
+
result: ReturnType<T> | null;
|
|
3
|
+
error: unknown;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Simple linear try catch utility to avoid repetitive try catch blocks
|
|
7
|
+
*/
|
|
8
|
+
declare function tryCatch<T extends () => any>(callback: T): TryCatchResult<T>;
|
|
9
|
+
export default tryCatch;
|
package/types.d.ts
CHANGED
|
@@ -1,12 +1,90 @@
|
|
|
1
1
|
export type { StateApi, ObservableFragment, MetadataSetter, StateChanges, StoreTools, ActionCollectionResult, GlobalStoreCallbacks, UseHookOptions, UnsubscribeCallback, SubscribeCallbackConfig, SubscribeCallback, BaseMetadata, MetadataGetter, SelectorCallback, SubscriberParameters, SubscriptionCallback, StateHook, ActionCollectionConfig, } from 'react-hooks-global-states/types';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @description Configuration for persisting state in localStorage
|
|
4
|
+
*/
|
|
5
|
+
export type LocalStorageConfig<State> = {
|
|
4
6
|
/**
|
|
5
|
-
* The
|
|
7
|
+
* @description The key used to store the item in localStorage.
|
|
6
8
|
*/
|
|
7
|
-
|
|
9
|
+
key: string;
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
11
|
+
* @description Validator function to ensure the integrity of the restored state.
|
|
12
|
+
* Receives the restored value and the initial state, must return a valid `State`.
|
|
13
|
+
* Executes after every initialization from localStorage, including after migration.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* validator: ({ restored, initial }) => {
|
|
18
|
+
* if (typeof restored !== 'number') {
|
|
19
|
+
* return initial;
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* return restored;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
10
25
|
*/
|
|
11
|
-
|
|
26
|
+
validator: (args: {
|
|
27
|
+
restored: unknown;
|
|
28
|
+
initial: State;
|
|
29
|
+
}) => State;
|
|
30
|
+
/**
|
|
31
|
+
* @description Error callback invoked when an exception occurs during any persistence phase.
|
|
32
|
+
* Use this to log or report issues without throwing.
|
|
33
|
+
*/
|
|
34
|
+
onError?: (error: unknown) => void;
|
|
35
|
+
versioning?: {
|
|
36
|
+
/**
|
|
37
|
+
* @description Current schema version for this item. When the stored version differs,
|
|
38
|
+
* the `migrator` function is invoked to upgrade the stored value.
|
|
39
|
+
* @default -1
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* {
|
|
43
|
+
* key: 'counter',
|
|
44
|
+
* version: 1
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
version: string | number;
|
|
49
|
+
/**
|
|
50
|
+
* @description Called when a stored value is found with a different version.
|
|
51
|
+
* Receives the raw stored value and must return the upgraded `State`.
|
|
52
|
+
* If and error is thrown during migration, the `onError` callback is invoked
|
|
53
|
+
* and the state falls back to the initial value.
|
|
54
|
+
*/
|
|
55
|
+
migrator: (args: {
|
|
56
|
+
legacy: unknown;
|
|
57
|
+
initial: State;
|
|
58
|
+
}) => State;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* @description High level overrides of the localstorage synchronization
|
|
62
|
+
* Use it if you want to have full control of how the state is stored/retrieved.
|
|
63
|
+
*
|
|
64
|
+
* This disables versioning, migration
|
|
65
|
+
*/
|
|
66
|
+
adapter?: {
|
|
67
|
+
/**
|
|
68
|
+
* @description Custom setter for the stored value associated with `key`.
|
|
69
|
+
*/
|
|
70
|
+
setItem: (key: string, value: State) => void;
|
|
71
|
+
/**
|
|
72
|
+
* @description Custom getter for the stored value associated with `key`.
|
|
73
|
+
* Should return the previously stored value (parsed/decoded to `State`) for that key.
|
|
74
|
+
*/
|
|
75
|
+
getItem: (key: string) => State;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* @description Structure of the item stored in localStorage
|
|
80
|
+
*/
|
|
81
|
+
export type ItemEnvelope<T> = {
|
|
82
|
+
/**
|
|
83
|
+
* @description Actual stored state
|
|
84
|
+
*/
|
|
85
|
+
s: T;
|
|
86
|
+
/**
|
|
87
|
+
* @description Version of the stored state
|
|
88
|
+
*/
|
|
89
|
+
v?: string | number;
|
|
12
90
|
};
|
package/webpack.config.js
CHANGED
|
@@ -7,15 +7,11 @@ const individualEntries = {
|
|
|
7
7
|
GlobalStore: './src/GlobalStore.ts',
|
|
8
8
|
GlobalStoreAbstract: './src/GlobalStoreAbstract.ts',
|
|
9
9
|
createGlobalState: './src/createGlobalState.ts',
|
|
10
|
-
types: './src/types.ts',
|
|
11
10
|
isRecord: './src/isRecord.ts',
|
|
12
11
|
shallowCompare: './src/shallowCompare.ts',
|
|
13
12
|
throwWrongKeyOnActionCollectionConfig: './src/throwWrongKeyOnActionCollectionConfig.ts',
|
|
13
|
+
types: './src/types.ts',
|
|
14
14
|
uniqueId: './src/uniqueId.ts',
|
|
15
|
-
|
|
16
|
-
// extras
|
|
17
|
-
getLocalStorageItem: './src/getLocalStorageItem.ts',
|
|
18
|
-
setLocalStorageItem: './src/setLocalStorageItem.ts',
|
|
19
15
|
};
|
|
20
16
|
|
|
21
17
|
const getExternalsForEntries = () => {
|
package/getLocalStorageItem.d.ts
DELETED
package/getLocalStorageItem.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var t;t=t=>(()=>{"use strict";var e={330:e=>{e.exports=t},527:(t,e,r)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.getLocalStorageItem=void 0;var o=r(330);e.getLocalStorageItem=function(t){var e=t.key;if(!e)return null;var r="function"==typeof e?e():e,a=localStorage.getItem(r);if(null===a)return null;var n=t.decrypt||t.encrypt?"function"==typeof t.decrypt?t.decrypt(a):atob(a):a;return(0,o.formatFromStore)(n,{jsonParse:!0})},e.default=e.getLocalStorageItem}},r={};return function t(o){var a=r[o];if(void 0!==a)return a.exports;var n=r[o]={exports:{}};return e[o](n,n.exports,t),n.exports}(527)})(),"object"==typeof exports&&"object"==typeof module?module.exports=t(require("json-storage-formatter/formatFromStore")):"function"==typeof define&&define.amd?define(["json-storage-formatter/formatFromStore"],t):"object"==typeof exports?exports["react-global-state-hooks"]=t(require("json-storage-formatter/formatFromStore")):this["react-global-state-hooks"]=t(this["json-storage-formatter/formatFromStore"]);
|
package/setLocalStorageItem.d.ts
DELETED
package/setLocalStorageItem.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var t;t=t=>(()=>{"use strict";var e={339:(t,e,o)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.setLocalStorageItem=void 0;var r=o(413);e.setLocalStorageItem=function(t,e){var o=e.key;if(o){var a="function"==typeof o?o():o,s=(0,r.formatToStore)(t,{stringify:!0,excludeTypes:["function"]}),f=e.encrypt?"function"==typeof e.encrypt?e.encrypt(s):btoa(s):s;localStorage.setItem(a,f)}},e.default=e.setLocalStorageItem},413:e=>{e.exports=t}},o={};return function t(r){var a=o[r];if(void 0!==a)return a.exports;var s=o[r]={exports:{}};return e[r](s,s.exports,t),s.exports}(339)})(),"object"==typeof exports&&"object"==typeof module?module.exports=t(require("json-storage-formatter/formatToStore")):"function"==typeof define&&define.amd?define(["json-storage-formatter/formatToStore"],t):"object"==typeof exports?exports["react-global-state-hooks"]=t(require("json-storage-formatter/formatToStore")):this["react-global-state-hooks"]=t(this["json-storage-formatter/formatToStore"]);
|