react-global-state-hooks 1.0.15 → 1.0.18
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 +390 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ import { useCountGlobal, sendCount } from './useCountGlobal'
|
|
|
128
128
|
const CountDisplayerComponent: React.FC = () => {
|
|
129
129
|
const [count] = useCountGlobal();
|
|
130
130
|
|
|
131
|
-
return (<
|
|
131
|
+
return (<label>{count}<label/>);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// here we have a separate component that is gonna handle the state of the previous component we created,
|
|
@@ -301,3 +301,392 @@ const store = new GlobalStore(0, {
|
|
|
301
301
|
...
|
|
302
302
|
|
|
303
303
|
...
|
|
304
|
+
|
|
305
|
+
# Examples and Comparison:
|
|
306
|
+
|
|
307
|
+
## 1. Lets try to share some state between components
|
|
308
|
+
|
|
309
|
+
### **With the GlobalStore approach, it will look like this:**
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
type TUser = {
|
|
313
|
+
name: string;
|
|
314
|
+
email: string;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const useUserStore = new GlobalStore<TUser>({
|
|
318
|
+
name: null,
|
|
319
|
+
email: null,
|
|
320
|
+
}).getHook();
|
|
321
|
+
|
|
322
|
+
const Component = () => {
|
|
323
|
+
const [currentUser] = useUserStore();
|
|
324
|
+
|
|
325
|
+
return <Text>{currentUser.name}</Text>;
|
|
326
|
+
};
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Simple, right?
|
|
330
|
+
|
|
331
|
+
### Let's now see how this same thing would look like by using context:
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
type TUser = {
|
|
335
|
+
name: string;
|
|
336
|
+
email: string;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const UserContext = createContext<{
|
|
340
|
+
currentUser: TUser;
|
|
341
|
+
}>({
|
|
342
|
+
currentUser: null,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const UserProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
346
|
+
const [currentUser, setCurrentUser] = useState<TUser>(null);
|
|
347
|
+
|
|
348
|
+
// ...get current user information
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<UserContext.Provider value={{ currentUser }}>
|
|
352
|
+
{children}
|
|
353
|
+
</UserContext.Provider>
|
|
354
|
+
);
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const Component = () => {
|
|
358
|
+
const { currentUser } = useContext(UserContext);
|
|
359
|
+
|
|
360
|
+
return <Text>{currentUser.name}</Text>;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const App = () => {
|
|
364
|
+
return (
|
|
365
|
+
<UserProvider>
|
|
366
|
+
<Component />
|
|
367
|
+
</UserProvider>
|
|
368
|
+
);
|
|
369
|
+
};
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### We already are able to notice a couple of extra lines right?
|
|
373
|
+
|
|
374
|
+
Let's now add another simple store to the equation
|
|
375
|
+
|
|
376
|
+
### **With the GlobalStore approach, it will look like this:**
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
type TUser = {
|
|
380
|
+
name: string;
|
|
381
|
+
email: string;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const useUserStore = new GlobalStore<TUser>({
|
|
385
|
+
name: null,
|
|
386
|
+
email: null,
|
|
387
|
+
}).getHook();
|
|
388
|
+
|
|
389
|
+
// we create the store
|
|
390
|
+
const useCountStore = new GlobalStore(0).getHook();
|
|
391
|
+
|
|
392
|
+
const Component = () => {
|
|
393
|
+
const [currentUser] = useUserStore();
|
|
394
|
+
|
|
395
|
+
// from the component we consume the new store
|
|
396
|
+
const [count, setCount] = useCountStore();
|
|
397
|
+
|
|
398
|
+
return <label>{currentUser.name}</label>;
|
|
399
|
+
};
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
With context, we'll have again to create all the boilerplate, and wrap the component into the new provider...
|
|
403
|
+
|
|
404
|
+
### **Lets see that**
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
type TUser = {
|
|
408
|
+
name: string;
|
|
409
|
+
email: string;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const UserContext = createContext<{
|
|
413
|
+
currentUser: TUser;
|
|
414
|
+
}>({
|
|
415
|
+
currentUser: null,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// let's create the context
|
|
419
|
+
const CountContext = createContext({
|
|
420
|
+
count: 0,
|
|
421
|
+
setCount: (() => {
|
|
422
|
+
throw new Error('not implemented');
|
|
423
|
+
}) as Dispatch<SetStateAction<number>>,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const UserProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
427
|
+
const [currentUser, setCurrentUser] = useState<TUser>(null);
|
|
428
|
+
|
|
429
|
+
// ...
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<UserContext.Provider value={{ currentUser }}>
|
|
433
|
+
{children}
|
|
434
|
+
</UserContext.Provider>
|
|
435
|
+
);
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// we also need another provider
|
|
439
|
+
const CountProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
440
|
+
const [count, setCount] = useState(0);
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<CountContext.Provider value={{ count, setCount }}>
|
|
444
|
+
{children}
|
|
445
|
+
</CountContext.Provider>
|
|
446
|
+
);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// we need to wrap the component into the new provider (this is for each future context)
|
|
450
|
+
const App = () => {
|
|
451
|
+
return (
|
|
452
|
+
<UserProvider>
|
|
453
|
+
<CountProvider>
|
|
454
|
+
<Component />
|
|
455
|
+
</CountProvider>
|
|
456
|
+
</UserProvider>
|
|
457
|
+
);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const Component = () => {
|
|
461
|
+
const { currentUser } = useContext(UserContext);
|
|
462
|
+
|
|
463
|
+
// finally we are able to get access to the new context...
|
|
464
|
+
const { count, setCount } = useContext(CountContext);
|
|
465
|
+
|
|
466
|
+
return <label>{currentUser.name}</label>;
|
|
467
|
+
};
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
In this example, we are able to see how every time along with creating a good amount of repetitive code, we also have to wrap the necessary components into the Provider... Also, notice how every time we need to modify the **App** component, even when the App component is not gonna use the new state.
|
|
471
|
+
|
|
472
|
+
### Let's make this a little more complex, now I want to implement custom methods for manipulating the count state, I also want to have the ability to modify the count state **without** having to be subscribed to the changes of the state... have you ever done that?
|
|
473
|
+
|
|
474
|
+
This is a common scenery, and guess what? in the **context** examples, we'll have to create another context, another provider, wrap and everything again...
|
|
475
|
+
|
|
476
|
+
## Let's see this time first the **context** approach
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
type TUser = {
|
|
480
|
+
name: string;
|
|
481
|
+
email: string;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const UserContext = createContext<{
|
|
485
|
+
currentUser: TUser;
|
|
486
|
+
}>({
|
|
487
|
+
currentUser: null,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// let's remove the setter from this context
|
|
491
|
+
const CountContext = createContext({
|
|
492
|
+
count: 0,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// lets create another context to share the actions
|
|
496
|
+
const CountContextSetter = createContext({
|
|
497
|
+
increase: (): void => {
|
|
498
|
+
throw new Error('increase is not implemented');
|
|
499
|
+
},
|
|
500
|
+
decrease: (): void => {
|
|
501
|
+
throw new Error('decrease is not implemented');
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const UserProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
506
|
+
const [currentUser, setCurrentUser] = useState<TUser>(null);
|
|
507
|
+
|
|
508
|
+
// ...
|
|
509
|
+
|
|
510
|
+
return (
|
|
511
|
+
<UserContext.Provider value={{ currentUser }}>
|
|
512
|
+
{children}
|
|
513
|
+
</UserContext.Provider>
|
|
514
|
+
);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// To don't overcomplicate the example let's just add but providers into this component, that will be enough
|
|
518
|
+
const CountProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
519
|
+
const [count, setCount] = useState(0);
|
|
520
|
+
|
|
521
|
+
const increase = () => setCount(count + 1);
|
|
522
|
+
const decrease = () => setCount(count - 1);
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
//one context is gonna share the edition of the state
|
|
526
|
+
<CountContext.Provider value={{ count }}>
|
|
527
|
+
{/* this second component will share the mutations of the state */}
|
|
528
|
+
<CountContextSetter.Provider value={{ increase, decrease }}>
|
|
529
|
+
{children}
|
|
530
|
+
</CountContextSetter.Provider>
|
|
531
|
+
</CountContext.Provider>
|
|
532
|
+
);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// Since we used the same provider we don't need to modify the **App** component, but we do are **Wrapping** everything into one more **Provider**
|
|
536
|
+
const App = () => {
|
|
537
|
+
return (
|
|
538
|
+
<UserProvider>
|
|
539
|
+
<CountProvider>
|
|
540
|
+
{/* lets create two componets instead of one */}
|
|
541
|
+
<ComponentSetter />
|
|
542
|
+
<Component />
|
|
543
|
+
</CountProvider>
|
|
544
|
+
</UserProvider>
|
|
545
|
+
);
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const ComponentSetter = () => {
|
|
549
|
+
const { increase, decrease } = useContext(CountContextSetter);
|
|
550
|
+
|
|
551
|
+
return (
|
|
552
|
+
<>
|
|
553
|
+
<button onPress={increase}>Increase</button>
|
|
554
|
+
<button onPress={decrease}>Decrease</button>
|
|
555
|
+
</>
|
|
556
|
+
);
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const Component = () => {
|
|
560
|
+
const { currentUser } = useContext(UserContext);
|
|
561
|
+
|
|
562
|
+
// finally we are able to get access to the new context...
|
|
563
|
+
const { count } = useContext(CountContext);
|
|
564
|
+
|
|
565
|
+
return (
|
|
566
|
+
<>
|
|
567
|
+
<label>{currentUser.name}</label>
|
|
568
|
+
<label>{count}</label>
|
|
569
|
+
</>
|
|
570
|
+
);
|
|
571
|
+
};
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Wow, a lot!!! just to be able to separate the mutations... and have mutations!!
|
|
575
|
+
|
|
576
|
+
### it would be easier with the GlobalStore? Let's see.
|
|
577
|
+
|
|
578
|
+
```tsx
|
|
579
|
+
type TUser = {
|
|
580
|
+
name: string;
|
|
581
|
+
email: string;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const useUser = new GlobalStore<TUser>({
|
|
585
|
+
name: null,
|
|
586
|
+
email: null,
|
|
587
|
+
}).getHook();
|
|
588
|
+
|
|
589
|
+
// let's modify the store to add custom actions, the second parameter is configuration let's just pass null for now
|
|
590
|
+
const countStore = new GlobalStore(0, null, {
|
|
591
|
+
increase() {
|
|
592
|
+
return ({ setState }: StoreTools<number>) => {
|
|
593
|
+
setState((state) => state + 1);
|
|
594
|
+
};
|
|
595
|
+
},
|
|
596
|
+
|
|
597
|
+
decrease() {
|
|
598
|
+
return ({ setState }: StoreTools<number>) => {
|
|
599
|
+
setState((state) => state - 1);
|
|
600
|
+
};
|
|
601
|
+
},
|
|
602
|
+
} as const);
|
|
603
|
+
|
|
604
|
+
const useCount = countStore.getHook();
|
|
605
|
+
|
|
606
|
+
// this actions don't use hooks, but are connected to the store and all the subscribers will be notified
|
|
607
|
+
const [, countActions] = countStore.getHookDecoupled();
|
|
608
|
+
|
|
609
|
+
// this component is not subscribed to the store, so it will not be notified when the state changes
|
|
610
|
+
const ComponentSetter = () => {
|
|
611
|
+
return (
|
|
612
|
+
<>
|
|
613
|
+
<button onPress={countActions.increase}>Increase</button>
|
|
614
|
+
<button onPress={countActions.decrease}>Decrease</button>
|
|
615
|
+
</>
|
|
616
|
+
);
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// this component is subscribed to the store, so it will be notified when the state changes
|
|
620
|
+
const Component = () => {
|
|
621
|
+
const [user] = useUser();
|
|
622
|
+
const [count, actions] = useCount();
|
|
623
|
+
|
|
624
|
+
return (
|
|
625
|
+
<>
|
|
626
|
+
<label>{count}</label>
|
|
627
|
+
</>
|
|
628
|
+
);
|
|
629
|
+
};
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### So let's analyze what happened
|
|
633
|
+
|
|
634
|
+
To restrict the state manipulations with the custom actions, we just need to add a third parameter to the store.
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
const countStore = new GlobalStore(0, null, {
|
|
638
|
+
log: (action: string) => () => console.log(action),
|
|
639
|
+
|
|
640
|
+
// every action is a function that returns a function that receives the store tools
|
|
641
|
+
increase() {
|
|
642
|
+
return ({ setState, getState }: StoreTools<number>): number => {
|
|
643
|
+
setState((state) => state + 1);
|
|
644
|
+
|
|
645
|
+
// actions are able to communicate between them
|
|
646
|
+
this.log('increase');
|
|
647
|
+
|
|
648
|
+
return getState();
|
|
649
|
+
};
|
|
650
|
+
},
|
|
651
|
+
} as const);
|
|
652
|
+
|
|
653
|
+
// the const is necessary to avoid typescript errors
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
All the library is strongly typed, we use generics to return the correct data type in each action.
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
const [, actions] = countStore.getHookDecoupled();
|
|
660
|
+
|
|
661
|
+
// for example the type of actions.increase will be: () => number
|
|
662
|
+
// just in case, even the parameters of the actions are gonna be exposed through TS
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
## getHookDecoupled
|
|
666
|
+
|
|
667
|
+
### **getHookDecoupled** returns a tuple with the state and the actions,
|
|
668
|
+
|
|
669
|
+
This is so useful when you want to use the actions without having to be subscribed to changes of the state.
|
|
670
|
+
There is also a third element in the tuple which is a function for getting the metadata of the store
|
|
671
|
+
|
|
672
|
+
### the metadata of the store is not reactive information which could be shared through the store
|
|
673
|
+
|
|
674
|
+
## Adding metadata to the store
|
|
675
|
+
|
|
676
|
+
```tsx
|
|
677
|
+
const [, , getMetadata] = new GlobalStore(0, {
|
|
678
|
+
metadata: {
|
|
679
|
+
isStoredSyncronized: false,
|
|
680
|
+
},
|
|
681
|
+
}).getHookDecoupled();
|
|
682
|
+
|
|
683
|
+
console.log(getMetadata().isStoredSyncronized); // false
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
The setMetadata is part of the store tools, so it can be used in the actions, but againg the metadata is not reactive!! so it will not trigger a re-render on the subscribers
|
|
687
|
+
|
|
688
|
+
...
|
|
689
|
+
|
|
690
|
+
...
|
|
691
|
+
|
|
692
|
+
# That's it for now!! hope you enjoy coding!!
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-global-state-hooks",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "This is a package to easily handling global-state across your react components No-redux, No-context.",
|
|
5
5
|
"main": "lib/GlobalStore.js",
|
|
6
6
|
"files": [
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"react-dom": "workspace:*"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"react-native-global-state-hooks": "^2.1.
|
|
63
|
+
"react-native-global-state-hooks": "^2.1.10"
|
|
64
64
|
}
|
|
65
65
|
}
|