react-global-state-hooks 7.0.0 → 7.0.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/README.md +123 -531
- package/getLocalStorageItem.js +1 -0
- package/package.json +12 -2
- package/setLocalStorageItem.js +1 -0
- package/webpack.config.js +2 -0
package/README.md
CHANGED
|
@@ -23,13 +23,21 @@ The best part? **react-hooks-global-states** is compatible with both **React** a
|
|
|
23
23
|
We are gonna create a global state hook **useCount** with one line of code.
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
|
-
import { createGlobalState } from 'react-global-state-hooks';
|
|
26
|
+
import { createGlobalState } from 'react-global-state-hooks/createGlobalState';
|
|
27
27
|
|
|
28
28
|
export const useCount = createGlobalState(0);
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
That's it! Welcome to global hooks. Now, you can use this state wherever you need it in your application.
|
|
32
32
|
|
|
33
|
+
The library is builded in modules, you can import everything or only what you need.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createGlobalState } from 'react-global-state-hooks/createGlobalState';
|
|
37
|
+
|
|
38
|
+
export const useCount = createGlobalState(0);
|
|
39
|
+
```
|
|
40
|
+
|
|
33
41
|
Let's see how to use it inside a simple **component**
|
|
34
42
|
|
|
35
43
|
```ts
|
|
@@ -115,8 +123,7 @@ Okay, everything works when the changes come from the state, but what happens if
|
|
|
115
123
|
const [filter, setFilter] = useState('');
|
|
116
124
|
|
|
117
125
|
const [contacts] = useContacts(
|
|
118
|
-
(state) =>
|
|
119
|
-
[...state.entities.values()].filter((item) => item.name.includes(filter)),
|
|
126
|
+
(state) => [...state.entities.values()].filter((item) => item.name.includes(filter)),
|
|
120
127
|
{
|
|
121
128
|
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
122
129
|
/**
|
|
@@ -137,12 +144,9 @@ export const useContacts = createGlobalState({
|
|
|
137
144
|
selected: Set<number>,
|
|
138
145
|
});
|
|
139
146
|
|
|
140
|
-
const useContactsArray = useContacts.createSelectorHook(
|
|
141
|
-
(
|
|
142
|
-
|
|
143
|
-
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
144
|
-
}
|
|
145
|
-
);
|
|
147
|
+
const useContactsArray = useContacts.createSelectorHook((state) => [...state.entities.values()], {
|
|
148
|
+
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
149
|
+
});
|
|
146
150
|
```
|
|
147
151
|
|
|
148
152
|
Now inside your component just call the new hook
|
|
@@ -152,39 +156,28 @@ Now inside your component just call the new hook
|
|
|
152
156
|
```tsx
|
|
153
157
|
const [filter, setFilter] = useState('');
|
|
154
158
|
|
|
155
|
-
const [contacts] = useContactsArray(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
dependencies: [filter],
|
|
159
|
-
}
|
|
160
|
-
);
|
|
159
|
+
const [contacts] = useContactsArray((entities) => entities.name.includes(filter), {
|
|
160
|
+
dependencies: [filter],
|
|
161
|
+
});
|
|
161
162
|
```
|
|
162
163
|
|
|
163
164
|
Or you can create another selectorHook from your **useContactsArray**
|
|
164
165
|
|
|
165
166
|
```ts
|
|
166
|
-
const useContactsArray = useContacts.createSelectorHook(
|
|
167
|
-
(
|
|
168
|
-
|
|
169
|
-
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
170
|
-
}
|
|
171
|
-
);
|
|
167
|
+
const useContactsArray = useContacts.createSelectorHook((state) => [...state.entities.values()], {
|
|
168
|
+
isEqualRoot: (a, b) => a.entities === b.entities,
|
|
169
|
+
});
|
|
172
170
|
|
|
173
|
-
const useContactsLength = useContactsArray.createSelectorHook(
|
|
174
|
-
(entities) => entities.length
|
|
175
|
-
);
|
|
171
|
+
const useContactsLength = useContactsArray.createSelectorHook((entities) => entities.length);
|
|
176
172
|
```
|
|
177
173
|
|
|
178
174
|
Or you can create a custom hook
|
|
179
175
|
|
|
180
176
|
```tsx
|
|
181
177
|
const useFilteredContacts = (filter: string) => {
|
|
182
|
-
const [contacts] = useContactsArray(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
dependencies: [filter],
|
|
186
|
-
}
|
|
187
|
-
);
|
|
178
|
+
const [contacts] = useContactsArray((entities) => entities.name.includes(filter), {
|
|
179
|
+
dependencies: [filter],
|
|
180
|
+
});
|
|
188
181
|
|
|
189
182
|
return contacts;
|
|
190
183
|
};
|
|
@@ -195,31 +188,28 @@ To summarize
|
|
|
195
188
|
```tsx
|
|
196
189
|
const [filter, setFilter] = useState('');
|
|
197
190
|
|
|
198
|
-
const [contacts] = useContacts(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*/
|
|
206
|
-
isEqualRoot: (r1, r2) => r1.filter === r2.filter,
|
|
191
|
+
const [contacts] = useContacts((state) => state.contacts.filter((contact) => contact.name.includes(filter)), {
|
|
192
|
+
/**
|
|
193
|
+
* You can use the `isEqualRoot` to validate if the values before the selector are equal.
|
|
194
|
+
* This validation will run before `isEqual` and if the result is true the selector will not be recomputed.
|
|
195
|
+
* If the result is true the re-render of the component will be prevented.
|
|
196
|
+
*/
|
|
197
|
+
isEqualRoot: (r1, r2) => r1.filter === r2.filter,
|
|
207
198
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
199
|
+
/**
|
|
200
|
+
* You can use the `isEqual` to validate if the values after the selector are equal.
|
|
201
|
+
* This validation will run after the selector computed a new value...
|
|
202
|
+
* and if the result is true it will prevent the re-render of the component.
|
|
203
|
+
*/
|
|
204
|
+
isEqual: (filter1, filter2) => filter1 === filter2,
|
|
214
205
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
206
|
+
/**
|
|
207
|
+
* You can use the `dependencies` array as with regular hooks to to force the recomputation of the selector.
|
|
208
|
+
* Is important ot mention that changes in the dependencies will not trigger a re-render of the component...
|
|
209
|
+
* Instead the recomputation of the selector will returned immediately.
|
|
210
|
+
*/
|
|
211
|
+
dependencies: [filter],
|
|
212
|
+
});
|
|
223
213
|
|
|
224
214
|
return (
|
|
225
215
|
<ul>
|
|
@@ -250,13 +240,9 @@ const useFilter = useContacts.createSelectorHook(({ filter }) => filter);
|
|
|
250
240
|
|
|
251
241
|
const useContactsArray = useContacts.createSelectorHook(({ items }) => items);
|
|
252
242
|
|
|
253
|
-
const useContactsLength = useContactsArray.createSelectorHook(
|
|
254
|
-
(items) => items.length
|
|
255
|
-
);
|
|
243
|
+
const useContactsLength = useContactsArray.createSelectorHook((items) => items.length);
|
|
256
244
|
|
|
257
|
-
const useIsContactsEmpty = useContactsLength.createSelectorHook(
|
|
258
|
-
(length) => !length
|
|
259
|
-
);
|
|
245
|
+
const useIsContactsEmpty = useContactsLength.createSelectorHook((length) => !length);
|
|
260
246
|
```
|
|
261
247
|
|
|
262
248
|
It can't get any simpler, right? Everything is connected, everything is reactive. Plus, these hooks are strongly typed, so if you're working with **TypeScript**, you'll absolutely love it.
|
|
@@ -270,32 +256,30 @@ Is common and often necessary to restrict the manipulation of state to a specifi
|
|
|
270
256
|
By defining a custom API for the **useContacts**, we can encapsulate and expose only the necessary actions or operations that are allowed to modify the state. This provides a controlled interface for interacting with the state, ensuring that modifications stick to the desired restrictions.
|
|
271
257
|
|
|
272
258
|
```ts
|
|
273
|
-
import { createGlobalState } from 'react-native-global-state-hooks';
|
|
274
|
-
|
|
275
|
-
const initialState = {
|
|
276
|
-
isLoading: true,
|
|
277
|
-
filter: '',
|
|
278
|
-
items: [] as Contact[],
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
type State = typeof initialState;
|
|
259
|
+
import { createGlobalState } from 'react-native-global-state-hooks/createGlobalState';
|
|
282
260
|
|
|
283
261
|
export const useContacts = createGlobalState(
|
|
284
|
-
initialState,
|
|
285
262
|
{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
},
|
|
263
|
+
isLoading: true,
|
|
264
|
+
filter: '',
|
|
265
|
+
items: [] as Contact[],
|
|
290
266
|
},
|
|
291
267
|
{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
268
|
+
callbacks: {
|
|
269
|
+
onInit: ({ setState }) => {
|
|
270
|
+
// fetch contacts
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
// this are the actions available for this state
|
|
274
|
+
actions: {
|
|
275
|
+
setFilter(filter: string) {
|
|
276
|
+
return ({ setState }) => {
|
|
277
|
+
setState((state) => ({
|
|
278
|
+
...state,
|
|
279
|
+
filter,
|
|
280
|
+
}));
|
|
281
|
+
};
|
|
282
|
+
},
|
|
299
283
|
},
|
|
300
284
|
}
|
|
301
285
|
);
|
|
@@ -334,7 +318,7 @@ It can't get any simpler, right? Everything is connected, everything is reactive
|
|
|
334
318
|
If you need to access the global state outside of a component or a hook without subscribing to state changes, or even inside a **ClassComponent**, you can use:
|
|
335
319
|
|
|
336
320
|
```tsx
|
|
337
|
-
useContacts.stateControls: () => [stateRetriever
|
|
321
|
+
useContacts.stateControls: () => [stateRetriever, stateMutator, Metadata];
|
|
338
322
|
|
|
339
323
|
// example:
|
|
340
324
|
const [getContacts, setContacts] = useContacts.stateControls();
|
|
@@ -355,18 +339,16 @@ Additionally, to subscribe to state changes, you can pass a callback function as
|
|
|
355
339
|
* This not only allows you to retrieve the current value of the state...
|
|
356
340
|
* but also enables you to subscribe to any changes in the state or a portion of it
|
|
357
341
|
*/
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
console.log('state changed: ', state);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
subscribe(
|
|
364
|
-
(state) => state.isLoading,
|
|
365
|
-
(isLoading) => {
|
|
366
|
-
console.log('is loading changed', isLoading);
|
|
367
|
-
}
|
|
368
|
-
);
|
|
342
|
+
const unsubscribe1 = contactsRetriever((state) => {
|
|
343
|
+
console.log('state changed: ', state);
|
|
369
344
|
});
|
|
345
|
+
|
|
346
|
+
const unsubscribe1 = contactsRetriever(
|
|
347
|
+
(state) => state.isLoading,
|
|
348
|
+
(isLoading) => {
|
|
349
|
+
console.log('is loading changed', isLoading);
|
|
350
|
+
}
|
|
351
|
+
);
|
|
370
352
|
```
|
|
371
353
|
|
|
372
354
|
That's great, isn't it? everything stays synchronized with the original state!!
|
|
@@ -380,15 +362,18 @@ Here's an example of adding multiple actions to the state and utilizing one acti
|
|
|
380
362
|
```ts
|
|
381
363
|
import { createGlobalState } from 'react-hooks-global-states';
|
|
382
364
|
|
|
383
|
-
export const useCount = createGlobalState(0,
|
|
365
|
+
export const useCount = createGlobalState(0, {
|
|
366
|
+
actions: {
|
|
384
367
|
log: (currentValue: string) => {
|
|
385
|
-
return ({ getState }
|
|
368
|
+
return ({ getState }): void => {
|
|
386
369
|
console.log(`Current Value: ${getState()}`);
|
|
387
370
|
};
|
|
388
371
|
},
|
|
389
372
|
|
|
390
373
|
increase(value: number = 1) {
|
|
391
|
-
return ({ getState, setState, actions }
|
|
374
|
+
return ({ getState, setState, actions }) => {
|
|
375
|
+
const [, actions] = useCount.stateControls();
|
|
376
|
+
|
|
392
377
|
setState((count) => count + value);
|
|
393
378
|
|
|
394
379
|
actions.log(message);
|
|
@@ -396,13 +381,16 @@ export const useCount = createGlobalState(0, () => ({{
|
|
|
396
381
|
},
|
|
397
382
|
|
|
398
383
|
decrease(value: number = 1) {
|
|
399
|
-
return ({ getState, setState, actions }
|
|
384
|
+
return ({ getState, setState, actions }) => {
|
|
385
|
+
const [, actions] = useCount.stateControls();
|
|
386
|
+
|
|
400
387
|
setState((count) => count - value);
|
|
401
388
|
|
|
402
389
|
actions.log(message);
|
|
403
390
|
};
|
|
404
391
|
},
|
|
405
|
-
}
|
|
392
|
+
},
|
|
393
|
+
});
|
|
406
394
|
```
|
|
407
395
|
|
|
408
396
|
Notice that the **StoreTools** will contain a reference to the generated actions API. From there, you'll be able to access all actions from inside another one... the **StoreTools** is generic and allow your to set an interface for getting the typing on the actions.
|
|
@@ -435,7 +423,7 @@ You can execute it immediately to subscribe to the state changes
|
|
|
435
423
|
const MyComponentInsideTheProvider = () => {
|
|
436
424
|
const [count] = useCounterContext()();
|
|
437
425
|
|
|
438
|
-
return
|
|
426
|
+
return <span>{count}</span>;
|
|
439
427
|
};
|
|
440
428
|
```
|
|
441
429
|
|
|
@@ -446,9 +434,7 @@ const MyComponent = () => {
|
|
|
446
434
|
// won't re-render if the counter changes
|
|
447
435
|
const [getCount, setCount] = useCounterContext().stateControls();
|
|
448
436
|
|
|
449
|
-
return (
|
|
450
|
-
<button onClick={() => setCount((count) => count + 1)}>Increase</button>
|
|
451
|
-
);
|
|
437
|
+
return <button onClick={() => setCount((count) => count + 1)}>Increase</button>;
|
|
452
438
|
};
|
|
453
439
|
```
|
|
454
440
|
|
|
@@ -472,354 +458,46 @@ const MyComponent = () => {
|
|
|
472
458
|
**createContext** also allows you to add custom actions to control the manipulation of the state inside the context
|
|
473
459
|
|
|
474
460
|
```tsx
|
|
475
|
-
import { createContext } from 'react-global-state-hooks';
|
|
476
|
-
|
|
477
|
-
type CounterState = {
|
|
478
|
-
count: number;
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
const initialState: CounterState = {
|
|
482
|
-
count: 0,
|
|
483
|
-
};
|
|
461
|
+
import { createContext } from 'react-global-state-hooks/createContext';
|
|
484
462
|
|
|
485
463
|
export const [useCounterContext, CounterProvider] = createStatefulContext(
|
|
486
|
-
initialState,
|
|
487
|
-
() => ({
|
|
488
|
-
increase: (value: number = 1) => {
|
|
489
|
-
return ({ setState }: StoreTools<CounterState>) => {
|
|
490
|
-
setState((state) => ({
|
|
491
|
-
...state,
|
|
492
|
-
count: state.count + value,
|
|
493
|
-
}));
|
|
494
|
-
};
|
|
495
|
-
},
|
|
496
|
-
decrease: (value: number = 1) => {
|
|
497
|
-
return ({ setState }: StoreTools<CounterState>) => {
|
|
498
|
-
setState((state) => ({
|
|
499
|
-
...state,
|
|
500
|
-
count: state.count - value,
|
|
501
|
-
}));
|
|
502
|
-
};
|
|
503
|
-
},
|
|
504
|
-
})
|
|
505
|
-
);
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
And just like with regular global hooks, now instead of a setState function, the hook will return the collection of actions
|
|
509
|
-
|
|
510
|
-
Last but not least, you can still creating **selectorHooks** with the **createSelectorHook** function, this hooks will only work if the if they are contained in the scope of the provider.
|
|
511
|
-
|
|
512
|
-
```tsx
|
|
513
|
-
const useIsEven = useCounterContext.createSelectorHook(
|
|
514
|
-
(count) => count % 2 === 0
|
|
515
|
-
);
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
# Emitters
|
|
519
|
-
|
|
520
|
-
So, we have seen that we can subscribe a callback to state changes, create **selector hooks** from our global states. Guess what? We can also create derived **emitters** and subscribe callbacks to specific portions of the state. Let's review it:
|
|
521
|
-
|
|
522
|
-
```ts
|
|
523
|
-
const subscribeToFilter = createDerivateEmitter(
|
|
524
|
-
contactsRetriever,
|
|
525
|
-
({ filter }) => ({
|
|
526
|
-
filter,
|
|
527
|
-
})
|
|
528
|
-
);
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
Cool, it's basically the same, but instead of using the **hook** as a parameter, we just have to use the **stateRetriever** as a parameter, and that will make the magic.
|
|
532
|
-
|
|
533
|
-
Now we are able to add a callback that will be executed every time the state of the **filter** changes.
|
|
534
|
-
|
|
535
|
-
```ts
|
|
536
|
-
const removeFilterSubscription = subscribeToFilter<Subscribe>(({ filter }) => {
|
|
537
|
-
console.log(`The filter value changed: ${filter}`);
|
|
538
|
-
});
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
By default, the callback will be executed once subscribed, using the current value of the state. If you want to avoid this initial call, you can pass an extra parameter to the **subscribe** function.
|
|
542
|
-
|
|
543
|
-
```ts
|
|
544
|
-
const removeFilterSubscription = subscribeToFilter<Subscribe>(
|
|
545
|
-
({ filter }) => {
|
|
546
|
-
console.log(`The filter value changed: ${filter}`);
|
|
547
|
-
},
|
|
548
464
|
{
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
);
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
Also, of course, if you have an exceptional case where you want to derived/selected directly from the current **emitter**, you can add a **selector**. This allows you to fine-tune the emitted values based on your requirements
|
|
555
|
-
|
|
556
|
-
```ts
|
|
557
|
-
const removeFilterSubscription = subscribeToFilter<Subscribe>(
|
|
558
|
-
({ filter }) => filter,
|
|
559
|
-
/**
|
|
560
|
-
* Cause of the selector the filter now is an string
|
|
561
|
-
*/
|
|
562
|
-
(filter) => {
|
|
563
|
-
console.log(`The filter value changed: ${filter}`);
|
|
465
|
+
count: 0,
|
|
564
466
|
},
|
|
565
467
|
{
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
468
|
+
actions: {
|
|
469
|
+
increase: (value: number = 1) => {
|
|
470
|
+
return ({ setState }) => {
|
|
471
|
+
setState((state) => ({
|
|
472
|
+
...state,
|
|
473
|
+
count: state.count + value,
|
|
474
|
+
}));
|
|
475
|
+
};
|
|
476
|
+
},
|
|
477
|
+
decrease: (value: number = 1) => {
|
|
478
|
+
return ({ setState }) => {
|
|
479
|
+
setState((state) => ({
|
|
480
|
+
...state,
|
|
481
|
+
count: state.count - value,
|
|
482
|
+
}));
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
},
|
|
573
486
|
}
|
|
574
487
|
);
|
|
575
488
|
```
|
|
576
489
|
|
|
577
|
-
And
|
|
578
|
-
|
|
579
|
-
```ts
|
|
580
|
-
const subscribeToItems = createDerivateEmitter(
|
|
581
|
-
contactsRetriever,
|
|
582
|
-
({ items }) => items
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
const subscribeToItemsLength = createDerivateEmitter(
|
|
586
|
-
subscribeToItems,
|
|
587
|
-
(items) => items.length
|
|
588
|
-
);
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
The examples may seem a little silly, but they allow you to see the incredible things you can accomplish with these **derived states** and **emitters**. They open up a world of possibilities!
|
|
592
|
-
|
|
593
|
-
# Combining stateRetriever
|
|
594
|
-
|
|
595
|
-
What if you have two states and you want to combine them? You may have already guessed it right? ... you can create combined **emitters** and **hooks** from the hook **stateRetriever**.
|
|
596
|
-
|
|
597
|
-
By utilizing the approach of combining **emitters** and **hooks**, you can effectively merge multiple states and make them shareable. This allows for better organization and simplifies the management of the combined states. You don't need to refactor everything; you just need to combine the **global state hooks** you already have. Let's see a simple example:
|
|
598
|
-
|
|
599
|
-
First we are gonna create a couple of **global states**, and extract the **stateRetriever**.
|
|
600
|
-
|
|
601
|
-
```ts
|
|
602
|
-
const useHook1 = createGlobalState({
|
|
603
|
-
propA: 1,
|
|
604
|
-
propB: 2,
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
const [stateRetriever1, stateMutator1] = useHook1.stateControls();
|
|
608
|
-
|
|
609
|
-
const useHook2 = createGlobalState({
|
|
610
|
-
propC: 3,
|
|
611
|
-
propD: 4,
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
const [, stateRetriever2] = useHook2.stateControls();
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
Okay, cool, the first state as **propA, propB** while the second one has **propC, propD**, let's combine them:
|
|
618
|
-
|
|
619
|
-
```ts
|
|
620
|
-
const [useCombinedHook, combinedStateRetriever] = combineAsyncGetters(
|
|
621
|
-
{
|
|
622
|
-
selector: ([state1, state2]) => ({
|
|
623
|
-
...state1,
|
|
624
|
-
...state2,
|
|
625
|
-
}),
|
|
626
|
-
},
|
|
627
|
-
stateRetriever1,
|
|
628
|
-
stateRetriever2
|
|
629
|
-
);
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
Well, that's it! Now you have access to a **combinedStateRetriever** that will return the combined value of the two states. From this new **combinedStateRetriever**, you can retrieve the value or subscribe to its changes. Let'see:
|
|
633
|
-
|
|
634
|
-
```ts
|
|
635
|
-
const value = stateRetriever(); // { propA, propB, propC, propD }
|
|
636
|
-
|
|
637
|
-
// subscribe to the new emitter
|
|
638
|
-
const unsubscribeGroup = stateRetriever<Subscribe>((subscribe) => {
|
|
639
|
-
subscribe((state) => {
|
|
640
|
-
console.log(subscribe); // full state
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
// Please note that if you add a selector,
|
|
644
|
-
// the callback will only trigger if the result of the selector changes.
|
|
645
|
-
subscribe(
|
|
646
|
-
({ propA, propD }) => ({ propA, propD }),
|
|
647
|
-
(derived) => {
|
|
648
|
-
console.log(derived); // { propA, propD }
|
|
649
|
-
}
|
|
650
|
-
);
|
|
651
|
-
});
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
Regarding the newly created hook, **useCombinedHook**, you can seamlessly utilize it across all your components, just like your other **global state hooks**. This enables a consistent and familiar approach for accessing and managing the combined state within your application.
|
|
655
|
-
|
|
656
|
-
```ts
|
|
657
|
-
const [combinedState] = useCombinedHook();
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
The main difference with **combined hooks** compared to individual **global state hooks** is the absence of **metadata** and **actions**. Instead, combined hooks provide a condensed representation of the underlying global states using simple React functionality. This streamlined approach ensures lightweight usage, making it easy to access and manage the combined state within your components.
|
|
661
|
-
|
|
662
|
-
### Let's explore some additional examples.
|
|
663
|
-
|
|
664
|
-
Similar to your other **global state hooks**, **combined hooks** allow you to use **selectors** directly from consumer components. This capability eliminates the need to create an excessive number of reusable hooks if they are not truly necessary. By utilizing selectors, you can efficiently extract specific data from the **combined state** and utilize it within your components. This approach offers a more concise and focused way of accessing the required state values without the need for creating additional hooks unnecessarily.
|
|
665
|
-
|
|
666
|
-
```ts
|
|
667
|
-
const [fragment] = useCombinedHook(({ propA, propD }) => ({ propA, propD }));
|
|
668
|
-
```
|
|
669
|
-
|
|
670
|
-
Lastly, you have the flexibility to continue combining stateRetrievers if desired. This means you can extend the functionality of combined hooks by adding more stateRetrievers to merge additional states. By combining stateRetrievers in this way, you can create a comprehensive and unified representation of the combined states within your application. This approach allows for modular and scalable state management, enabling you to efficiently handle complex state compositions.
|
|
671
|
-
|
|
672
|
-
Let's see an example:
|
|
673
|
-
|
|
674
|
-
```ts
|
|
675
|
-
const [useCombinedHook, combinedStateRetriever1] = combineAsyncGetters(
|
|
676
|
-
{
|
|
677
|
-
selector: ([state1, state2]) => ({
|
|
678
|
-
...state1,
|
|
679
|
-
...state2,
|
|
680
|
-
}),
|
|
681
|
-
},
|
|
682
|
-
stateRetriever1,
|
|
683
|
-
stateRetriever2
|
|
684
|
-
);
|
|
685
|
-
|
|
686
|
-
const useHook3 = createGlobalState({
|
|
687
|
-
propE: 1,
|
|
688
|
-
propF: 2,
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
const [stateRetriever3, stateMutator3] = useHook3.stateControls();
|
|
692
|
-
|
|
693
|
-
const useIsLoading = createGlobalState(false);
|
|
694
|
-
|
|
695
|
-
const [isLoadingStateRetriever, isLoadingMutator] =
|
|
696
|
-
useIsLoading.stateControls();
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
Once we created another peace of state, we can combine it with our other **global hooks** and **emitters**
|
|
700
|
-
|
|
701
|
-
```ts
|
|
702
|
-
const [useCombinedHook2, combinedStateRetriever2] = combineAsyncGetters(
|
|
703
|
-
{
|
|
704
|
-
selector: ([state1, state2, isLoading]) => ({
|
|
705
|
-
...state1,
|
|
706
|
-
...state2,
|
|
707
|
-
isLoading,
|
|
708
|
-
}),
|
|
709
|
-
},
|
|
710
|
-
combinedStateRetriever1,
|
|
711
|
-
stateRetriever3,
|
|
712
|
-
isLoadingStateRetriever
|
|
713
|
-
);
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
You have the freedom to combine as many global hooks as you wish. This means you can merge multiple states into a single cohesive unit by combining their respective hooks. This approach offers flexibility and scalability, allowing you to handle complex state compositions in a modular and efficient manner.
|
|
717
|
-
|
|
718
|
-
### **Quick note**:
|
|
719
|
-
|
|
720
|
-
Please be aware that the third parameter is a **dispose callback**, which can be particularly useful in **high-order** functions when you want to release any resources associated with the hook. By invoking the dispose callback, the hook will no longer report any changes, ensuring that resources are properly cleaned up. This allows for efficient resource management and can be beneficial in scenarios where you need to handle resource cleanup or termination in a controlled manner.
|
|
721
|
-
|
|
722
|
-
# Extending Global Hooks
|
|
723
|
-
|
|
724
|
-
Creating a custom builder for your **global hook** is made incredibly easy with the **createCustomGlobalState** function.
|
|
725
|
-
|
|
726
|
-
This function returns a new global state builder wrapped with the desired custom implementation, allowing you to get creative! Le'ts see and example:
|
|
727
|
-
|
|
728
|
-
```ts
|
|
729
|
-
import { formatFromStore, formatToStore, createCustomGlobalState } = 'react-global-state-hooks'
|
|
730
|
-
|
|
731
|
-
// Optional configuration available for the consumers of the builder
|
|
732
|
-
type HookConfig = {
|
|
733
|
-
asyncStorageKey?: string;
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
// This is the base metadata that all the stores created from the builder will have.
|
|
737
|
-
type BaseMetadata = {
|
|
738
|
-
isAsyncStorageReady?: boolean;
|
|
739
|
-
};
|
|
740
|
-
|
|
741
|
-
export const createGlobalState = createCustomGlobalState<
|
|
742
|
-
BaseMetadata,
|
|
743
|
-
HookConfig
|
|
744
|
-
>({
|
|
745
|
-
/**
|
|
746
|
-
* This function executes immediately after the global state is created, before the invocations of the hook
|
|
747
|
-
*/
|
|
748
|
-
onInitialize: async ({ setState, setMetadata }, config) => {
|
|
749
|
-
setMetadata((metadata) => ({
|
|
750
|
-
...(metadata ?? {}),
|
|
751
|
-
isAsyncStorageReady: null,
|
|
752
|
-
}));
|
|
753
|
-
|
|
754
|
-
const asyncStorageKey = config?.asyncStorageKey;
|
|
755
|
-
if (!asyncStorageKey) return;
|
|
756
|
-
|
|
757
|
-
const storedItem = (await asyncStorage.getItem(asyncStorageKey)) as string;
|
|
758
|
-
|
|
759
|
-
// update the metadata, remember, metadata is not reactive
|
|
760
|
-
setMetadata((metadata) => ({
|
|
761
|
-
...metadata,
|
|
762
|
-
isAsyncStorageReady: true,
|
|
763
|
-
}));
|
|
764
|
-
|
|
765
|
-
if (storedItem === null) {
|
|
766
|
-
return setState((state) => state, { forceUpdate: true });
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const parsed = formatFromStore(storedItem, {
|
|
770
|
-
jsonParse: true,
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
setState(parsed, { forceUpdate: true });
|
|
774
|
-
},
|
|
775
|
-
|
|
776
|
-
onChange: ({ getState }, config) => {
|
|
777
|
-
if (!config?.asyncStorageKey) return;
|
|
778
|
-
|
|
779
|
-
const state = getState();
|
|
780
|
-
|
|
781
|
-
const formattedObject = formatToStore(state, {
|
|
782
|
-
stringify: true,
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
asyncStorage.setItem(config.asyncStorageKey, formattedObject);
|
|
786
|
-
},
|
|
787
|
-
});
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
It is important to use **forceUpdate** to force React to re-render our components and obtain the most recent state of the **metadata**. This is especially useful when working with primitive types, as it can be challenging to differentiate between a primitive value that originates from storage and one that does not.
|
|
791
|
-
|
|
792
|
-
It is worth mentioning that the **onInitialize** function will be executed only once per global state.
|
|
793
|
-
|
|
794
|
-
You can use to **formatToStore**, and **formatFromStore** to sanitize your data, These methods will help you transform objects into JSON strings and retrieve them back without losing any of the original data types. You will no longer encounter problems when **stringifying** Dates, Maps, Sets, and other complex data types. You could take a look in the API here: [json-storage-formatter](https://www.npmjs.com/package/json-storage-formatter).
|
|
795
|
-
|
|
796
|
-
Let's see how to create a global state using our new builder:
|
|
797
|
-
|
|
798
|
-
```ts
|
|
799
|
-
const useTodos = createGlobalState(new Map<string, number>(), {
|
|
800
|
-
config: {
|
|
801
|
-
asyncStorageKey: 'todos',
|
|
802
|
-
},
|
|
803
|
-
});
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
That's correct! If you add an **asyncStorageKey** to the state configuration, the state will be synchronized with the **asyncStorage**
|
|
807
|
-
|
|
808
|
-
Let's see how to use this async storage hook into our components:
|
|
490
|
+
And just like with regular global hooks, now instead of a setState function, the hook will return the collection of actions
|
|
809
491
|
|
|
810
|
-
|
|
811
|
-
const [todos, setTodos, metadata] = useTodos();
|
|
492
|
+
Last but not least, you can still creating **selectorHooks** with the **createSelectorHook** function, this hooks will only work if the if they are contained in the scope of the provider.
|
|
812
493
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
<>);
|
|
494
|
+
```tsx
|
|
495
|
+
const useIsEven = useCounterContext.createSelectorHook((count) => count % 2 === 0);
|
|
816
496
|
```
|
|
817
497
|
|
|
818
|
-
The **metadata** is not reactive information and can only be modified from inside the global state lifecycle methods.
|
|
819
|
-
|
|
820
498
|
# Life cycle methods
|
|
821
499
|
|
|
822
|
-
There are some lifecycle methods available for use with global hooks, let's review them:
|
|
500
|
+
There are some lifecycle methods available for use with global hooks, let's review them callbacks prop:
|
|
823
501
|
|
|
824
502
|
```ts
|
|
825
503
|
/**
|
|
@@ -861,7 +539,7 @@ onInit?: ({
|
|
|
861
539
|
/**
|
|
862
540
|
* @description - callback function called every time the state is changed
|
|
863
541
|
*/
|
|
864
|
-
onStateChanged?: (parameters:
|
|
542
|
+
onStateChanged?: (parameters: StoreTools<any, any> & StateChanges<unknown>) => void;
|
|
865
543
|
|
|
866
544
|
/**
|
|
867
545
|
* callback function called every time a component is subscribed to the store
|
|
@@ -871,7 +549,7 @@ onSubscribed?: (parameters: StateConfigCallbackParam<TState, TMetadata, TActions
|
|
|
871
549
|
/**
|
|
872
550
|
* callback function called every time the state is about to change and it allows you to prevent the state change
|
|
873
551
|
*/
|
|
874
|
-
computePreventStateChange?: (parameters:
|
|
552
|
+
computePreventStateChange?: (parameters: StoreTools<any, any> & StateChanges<unknown>) => boolean;
|
|
875
553
|
```
|
|
876
554
|
|
|
877
555
|
You can pass this callbacks between on the second parameter of the builders like **createGlobalState**
|
|
@@ -883,12 +561,14 @@ const useData = createGlobalState(
|
|
|
883
561
|
metadata: {
|
|
884
562
|
someExtraInformation: 'someExtraInformation',
|
|
885
563
|
},
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
564
|
+
callbacks: {
|
|
565
|
+
// onSubscribed: (StateConfigCallbackParam) => {},
|
|
566
|
+
// onInit // etc
|
|
567
|
+
computePreventStateChange: ({ state, previousState }) => {
|
|
568
|
+
const prevent = isEqual(state, previousState);
|
|
569
|
+
|
|
570
|
+
return prevent;
|
|
571
|
+
},
|
|
892
572
|
},
|
|
893
573
|
}
|
|
894
574
|
);
|
|
@@ -899,95 +579,7 @@ Finally, if you have a very specific necessity but still want to use the global
|
|
|
899
579
|
Let's see an example again with the **asyncStorage** custom global hook but with the abstract class.
|
|
900
580
|
|
|
901
581
|
```ts
|
|
902
|
-
|
|
903
|
-
TState,
|
|
904
|
-
TMetadata extends {
|
|
905
|
-
asyncStorageKey?: string;
|
|
906
|
-
isAsyncStorageReady?: boolean;
|
|
907
|
-
} | null = null,
|
|
908
|
-
TStateSetter extends
|
|
909
|
-
| ActionCollectionConfig<TState, TMetadata>
|
|
910
|
-
| StateSetter<TState> = StateSetter<TState>
|
|
911
|
-
> extends GlobalStoreAbstract<TState, TMetadata, TStateSetter> {
|
|
912
|
-
constructor(
|
|
913
|
-
state: TState,
|
|
914
|
-
config: GlobalStoreConfig<TState, TMetadata, TStateSetter> = {},
|
|
915
|
-
actionsConfig: TStateSetter | null = null
|
|
916
|
-
) {
|
|
917
|
-
super(state, config, actionsConfig);
|
|
918
|
-
|
|
919
|
-
this.initialize();
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
protected onInitialize = async ({
|
|
923
|
-
setState,
|
|
924
|
-
setMetadata,
|
|
925
|
-
getMetadata,
|
|
926
|
-
getState,
|
|
927
|
-
}: StateConfigCallbackParam<TState, TMetadata, TStateSetter>) => {
|
|
928
|
-
setMetadata({
|
|
929
|
-
...(metadata ?? {}),
|
|
930
|
-
isAsyncStorageReady: null,
|
|
931
|
-
});
|
|
932
|
-
|
|
933
|
-
const metadata = getMetadata();
|
|
934
|
-
const asyncStorageKey = metadata?.asyncStorageKey;
|
|
935
|
-
|
|
936
|
-
if (!asyncStorageKey) return;
|
|
937
|
-
|
|
938
|
-
const storedItem = (await asyncStorage.getItem(asyncStorageKey)) as string;
|
|
939
|
-
setMetadata({
|
|
940
|
-
...metadata,
|
|
941
|
-
isAsyncStorageReady: true,
|
|
942
|
-
});
|
|
943
|
-
|
|
944
|
-
if (storedItem === null) {
|
|
945
|
-
const state = getState();
|
|
946
|
-
|
|
947
|
-
// force the re-render of the subscribed components even if the state is the same
|
|
948
|
-
return setState(state, { forceUpdate: true });
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
const items = formatFromStore<TState>(storedItem, {
|
|
952
|
-
jsonParse: true,
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
setState(items, { forceUpdate: true });
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
protected onChange = ({
|
|
959
|
-
getMetadata,
|
|
960
|
-
getState,
|
|
961
|
-
}: StateChangesParam<TState, TMetadata, NonNullable<TStateSetter>>) => {
|
|
962
|
-
const asyncStorageKey = getMetadata()?.asyncStorageKey;
|
|
963
|
-
|
|
964
|
-
if (!asyncStorageKey) return;
|
|
965
|
-
|
|
966
|
-
const state = getState();
|
|
967
|
-
|
|
968
|
-
const formattedObject = formatToStore(state, {
|
|
969
|
-
stringify: true,
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
asyncStorage.setItem(asyncStorageKey, formattedObject);
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
Then, from an instance of the global store, you will be able to access the hooks.
|
|
978
|
-
|
|
979
|
-
```ts
|
|
980
|
-
const storage = new GlobalStore(0, {
|
|
981
|
-
metadata: {
|
|
982
|
-
asyncStorageKey: 'counter',
|
|
983
|
-
isAsyncStorageReady: false,
|
|
984
|
-
},
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
const [getState, _, getMetadata] = storage.stateControls();
|
|
988
|
-
const useState = storage.getHook();
|
|
582
|
+
extends GlobalStoreAbstract<TState, TMetadata, TStateSetter>
|
|
989
583
|
```
|
|
990
584
|
|
|
991
|
-
### **Note**: The GlobalStore class is still available in the package in case you were already extending from it.
|
|
992
|
-
|
|
993
585
|
# That's it for now!! hope you enjoy coding!!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(r,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["react-global-state-hooks"]=t():r["react-global-state-hooks"]=t()}(this,(()=>(()=>{var r={129:function(r){r.exports=(()=>{"use strict";var r={333:(r,t,e)=>{function n(r){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r},n(r)}function o(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=Array(t);e<t;e++)n[e]=r[e];return n}Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0;var i=e(646),u=e(346),a=e(473);t.clone=function(r){var e,l=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).shallow;if((0,a.isPrimitive)(r)||(0,i.isDate)(r))return r;if(Array.isArray(r))return l?function(r){if(Array.isArray(r))return o(r)}(e=r)||function(r){if("undefined"!=typeof Symbol&&null!=r[Symbol.iterator]||null!=r["@@iterator"])return Array.from(r)}(e)||function(r,t){if(r){if("string"==typeof r)return o(r,t);var e={}.toString.call(r).slice(8,-1);return"Object"===e&&r.constructor&&(e=r.constructor.name),"Map"===e||"Set"===e?Array.from(r):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?o(r,t):void 0}}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}():r.map((function(r){return(0,t.clone)(r)}));if(r instanceof Map){var f=Array.from(r.entries());return l?new Map(f):new Map(f.map((function(r){return(0,t.clone)(r)})))}if(r instanceof Set){var c=Array.from(r.values());return l?new Set(c):new Set(c.map((function(r){return(0,t.clone)(r)})))}return r instanceof RegExp?new RegExp(r.toString()):(0,u.isFunction)(r)?l?r:Object.create(r):l?Object.assign({},r):r instanceof Error?new Error(r.message):Object.keys(r).reduce((function(e,o){var i=r[o];return Object.assign(Object.assign({},e),function(r,t,e){return(t=function(r){var t=function(r){if("object"!=n(r)||!r)return r;var t=r[Symbol.toPrimitive];if(void 0!==t){var e=t.call(r,"string");if("object"!=n(e))return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}(r);return"symbol"==n(t)?t:t+""}(t))in r?Object.defineProperty(r,t,{value:e,enumerable:!0,configurable:!0,writable:!0}):r[t]=e,r}({},o,(0,t.clone)(i)))}),{})}},944:(r,t,e)=>{function n(r){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r},n(r)}function o(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=Array(t);e<t;e++)n[e]=r[e];return n}Object.defineProperty(t,"__esModule",{value:!0}),t.formatFromStore=void 0;var i=e(333),u=e(346),a=e(473);t.formatFromStore=function(r){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},l=e.jsonParse,f=e.sortKeys;return function(r){var e,i,l;if((0,a.isPrimitive)(r))return r;if("date"===(null==r?void 0:r.$t))return new Date(r.$v);if("map"===(null==r?void 0:r.$t)){var c=(null!==(e=r.$v)&&void 0!==e?e:[]).map((function(r){var e,n,i=(n=2,function(r){if(Array.isArray(r))return r}(e=r)||function(r,t){var e=null==r?null:"undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(null!=e){var n,o,i,u,a=[],l=!0,f=!1;try{if(i=(e=e.call(r)).next,0===t){if(Object(e)!==e)return;l=!1}else for(;!(l=(n=i.call(e)).done)&&(a.push(n.value),a.length!==t);l=!0);}catch(r){f=!0,o=r}finally{try{if(!l&&null!=e.return&&(u=e.return(),Object(u)!==u))return}finally{if(f)throw o}}return a}}(e,n)||function(r,t){if(r){if("string"==typeof r)return o(r,t);var e={}.toString.call(r).slice(8,-1);return"Object"===e&&r.constructor&&(e=r.constructor.name),"Map"===e||"Set"===e?Array.from(r):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?o(r,t):void 0}}(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),u=i[0],a=i[1];return[u,(0,t.formatFromStore)(a)]}));return new Map(c)}if("set"===(null==r?void 0:r.$t)){var s=null!==(i=r.$v)&&void 0!==i?i:[].map((function(r){return(0,t.formatFromStore)(r)}));return new Set(s)}return"regex"===(null==r?void 0:r.$t)?new RegExp(r.$v):"error"===(null==r?void 0:r.$t)?new Error(r.$v):Array.isArray(r)?r.map((function(r){return(0,t.formatFromStore)(r)})):"function"===(null==r?void 0:r.$t)?Function("(".concat(r.$v,")(...arguments)")):(l=Object.keys(r),f?(0,u.isFunction)(f)?l.sort(f):l.sort((function(r,t){return(null!=r?r:"").localeCompare(t)})):l).reduce((function(e,o){var i,u,a,l=r[o];return Object.assign(Object.assign({},e),(i={},u=o,a=(0,t.formatFromStore)(l),(u=function(r){var t=function(r){if("object"!=n(r)||!r)return r;var t=r[Symbol.toPrimitive];if(void 0!==t){var e=t.call(r,"string");if("object"!=n(e))return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}(r);return"symbol"==n(t)?t:t+""}(u))in i?Object.defineProperty(i,u,{value:a,enumerable:!0,configurable:!0,writable:!0}):i[u]=a,i))}),{})}(l?JSON.parse(r):(0,i.clone)(r))}},112:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isBoolean=void 0,t.isBoolean=function(r){return"boolean"==typeof r}},646:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isDate=void 0,t.isDate=function(r){return r instanceof Date}},346:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isFunction=void 0,t.isFunction=function(r){return"function"==typeof r||r instanceof Function}},243:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isNil=void 0,t.isNil=function(r){return null==r}},11:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isNumber=void 0,t.isNumber=function(r){return"number"==typeof r}},473:(r,t,e)=>{function n(r){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r},n(r)}Object.defineProperty(t,"__esModule",{value:!0}),t.isPrimitive=void 0;var o=e(112),i=e(243),u=e(11),a=e(671);t.isPrimitive=function(r){return(0,i.isNil)(r)||(0,u.isNumber)(r)||(0,o.isBoolean)(r)||(0,a.isString)(r)||"symbol"===n(r)}},671:(r,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isString=void 0,t.isString=function(r){return"string"==typeof r}}},t={};return function e(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return r[n](i,i.exports,e),i.exports}(944)})()}},t={};function e(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return r[n].call(i.exports,i,i.exports,e),i.exports}var n={};return(()=>{"use strict";var r=n;Object.defineProperty(r,"__esModule",{value:!0}),r.getLocalStorageItem=void 0;var t=e(129);r.getLocalStorageItem=function(r){var e=r.key;if(!e)return null;var n="function"==typeof e?e():e,o=localStorage.getItem(n);if(null===o)return null;var i=r.decrypt||r.encrypt?"function"==typeof r.decrypt?r.decrypt(o):atob(o):o;return(0,t.formatFromStore)(i,{jsonParse:!0})}})(),n})()));
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-global-state-hooks",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "This is a package to easily handling global-state across your react components No-redux, No-context.",
|
|
5
5
|
"main": "./bundle.js",
|
|
6
|
-
"types": "./
|
|
6
|
+
"types": "./index.d.ts",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"require": "./debounce.js",
|
|
57
57
|
"types": "./debounce.d.ts"
|
|
58
58
|
},
|
|
59
|
+
"./getLocalStorageItem": {
|
|
60
|
+
"import": "./getLocalStorageItem.js",
|
|
61
|
+
"require": "./getLocalStorageItem.js",
|
|
62
|
+
"types": "./getLocalStorageItem.d.ts"
|
|
63
|
+
},
|
|
64
|
+
"./setLocalStorageItem": {
|
|
65
|
+
"import": "./setLocalStorageItem.js",
|
|
66
|
+
"require": "./setLocalStorageItem.js",
|
|
67
|
+
"types": "./setLocalStorageItem.d.ts"
|
|
68
|
+
},
|
|
59
69
|
"./isRecord": {
|
|
60
70
|
"import": "./isRecord.js",
|
|
61
71
|
"require": "./isRecord.js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["react-global-state-hooks"]=t():e["react-global-state-hooks"]=t()}(this,(()=>(()=>{var e={75:function(e){e.exports=(()=>{"use strict";var e={333:(e,t,r)=>{function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0;var i=r(646),u=r(346),a=r(473);t.clone=function(e){var r,f=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).shallow;if((0,a.isPrimitive)(e)||(0,i.isDate)(e))return e;if(Array.isArray(e))return f?function(e){if(Array.isArray(e))return o(e)}(r=e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(r)||function(e,t){if(e){if("string"==typeof e)return o(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?o(e,t):void 0}}(r)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}():e.map((function(e){return(0,t.clone)(e)}));if(e instanceof Map){var c=Array.from(e.entries());return f?new Map(c):new Map(c.map((function(e){return(0,t.clone)(e)})))}if(e instanceof Set){var s=Array.from(e.values());return f?new Set(s):new Set(s.map((function(e){return(0,t.clone)(e)})))}return e instanceof RegExp?new RegExp(e.toString()):(0,u.isFunction)(e)?f?e:Object.create(e):f?Object.assign({},e):e instanceof Error?new Error(e.message):Object.keys(e).reduce((function(r,o){var i=e[o];return Object.assign(Object.assign({},r),function(e,t,r){return(t=function(e){var t=function(e){if("object"!=n(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var r=t.call(e,"string");if("object"!=n(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==n(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}({},o,(0,t.clone)(i)))}),{})}},112:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isBoolean=void 0,t.isBoolean=function(e){return"boolean"==typeof e}},646:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isDate=void 0,t.isDate=function(e){return e instanceof Date}},346:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isFunction=void 0,t.isFunction=function(e){return"function"==typeof e||e instanceof Function}},243:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isNil=void 0,t.isNil=function(e){return null==e}},11:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isNumber=void 0,t.isNumber=function(e){return"number"==typeof e}},473:(e,t,r)=>{function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.isPrimitive=void 0;var o=r(112),i=r(243),u=r(11),a=r(671);t.isPrimitive=function(e){return(0,i.isNil)(e)||(0,u.isNumber)(e)||(0,o.isBoolean)(e)||(0,a.isString)(e)||"symbol"===n(e)}},43:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isRegex=void 0,t.isRegex=function(e){return e instanceof RegExp}},671:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isString=void 0,t.isString=function(e){return"string"==typeof e}}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,r),i.exports}var n={};return(()=>{var e=n;function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t(e)}Object.defineProperty(e,"__esModule",{value:!0}),e.formatToStore=void 0;var o=r(333),i=r(646),u=r(346),a=r(473),f=r(43);e.formatToStore=function(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{stringify:!1},n=r.stringify,c=r.validator,s=r.excludeTypes,l=r.excludeKeys,y=r.sortKeys,v=new Set(null!=s?s:[]),p=new Set(null!=l?l:[]),m=v.size||p.size,b=null!=c?c:function(e){var r=e.key,n=e.value;if(!m)return!0;var o=p.has(r),i=v.has(t(n));return!o&&!i},d=function(e){if((0,a.isPrimitive)(e))return e;var r;if(Array.isArray(e))return e.map((function(e){return d(e)}));if(e instanceof Map)return{$t:"map",$v:Array.from(e.entries()).map((function(e){return d(e)}))};if(e instanceof Set)return{$t:"set",$v:Array.from(e.values()).map((function(e){return d(e)}))};if((0,i.isDate)(e))return{$t:"date",$v:e.toISOString()};if((0,f.isRegex)(e))return{$t:"regex",$v:e.toString()};if((0,u.isFunction)(e)){var n;try{n={$t:"function",$v:e.toString()}}catch(e){n={$t:"error",$v:"Error: Could not serialize function"}}return n}return e instanceof Error?{$t:"error",$v:e.message}:(r=Object.keys(e),y?(0,u.isFunction)(y)?r.sort(y):r.sort((function(e,t){return(null!=e?e:"").localeCompare(t)})):r).reduce((function(r,n){var o,i,u,a=e[n],f=d(a);return b({obj:e,key:n,value:f})?Object.assign(Object.assign({},r),(o={},i=n,u=d(a),(i=function(e){var r=function(e){if("object"!=t(e)||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,"string");if("object"!=t(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==t(r)?r:r+""}(i))in o?Object.defineProperty(o,i,{value:u,enumerable:!0,configurable:!0,writable:!0}):o[i]=u,o)):r}),{})},S=d((0,o.clone)(e));return n?JSON.stringify(S):S}})(),n})()}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return e[n].call(i.exports,i,i.exports,r),i.exports}var n={};return(()=>{"use strict";var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.setLocalStorageItem=void 0;var t=r(75);e.setLocalStorageItem=function(e,r){var n=r.key;if(n){var o="function"==typeof n?n():n,i=(0,t.formatToStore)(e,{stringify:!0,excludeTypes:["function"]}),u=r.encrypt?"function"==typeof r.encrypt?r.encrypt(i):btoa(i):i;localStorage.setItem(o,u)}}})(),n})()));
|
package/webpack.config.js
CHANGED
|
@@ -22,6 +22,8 @@ module.exports = {
|
|
|
22
22
|
uniqueSymbol: './src/uniqueSymbol.ts',
|
|
23
23
|
useConstantValueRef: './src/useConstantValueRef.ts',
|
|
24
24
|
// extras
|
|
25
|
+
getLocalStorageItem: './src/getLocalStorageItem.ts',
|
|
26
|
+
setLocalStorageItem: './src/setLocalStorageItem.ts',
|
|
25
27
|
},
|
|
26
28
|
externals: {
|
|
27
29
|
react: 'react',
|