state-decorator 7.0.0-beta.1 → 7.0.0-beta.11-dev

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.
Files changed (71) hide show
  1. package/README.md +271 -169
  2. package/es/helpers.d.ts +20 -2
  3. package/es/helpers.js +162 -1
  4. package/es/helpers.js.map +1 -1
  5. package/es/impl.d.ts +32 -20
  6. package/es/impl.js +974 -1
  7. package/es/impl.js.map +1 -1
  8. package/es/index.d.ts +31 -41
  9. package/es/index.js +1026 -1
  10. package/es/index.js.map +1 -1
  11. package/es/middlewares.d.ts +1 -1
  12. package/es/middlewares.js +519 -1
  13. package/es/middlewares.js.map +1 -1
  14. package/es/test.d.ts +10 -8
  15. package/es/test.js +339 -1
  16. package/es/test.js.map +1 -1
  17. package/es/types.d.ts +66 -50
  18. package/es/v5.js +301 -1
  19. package/es/v5.js.map +1 -1
  20. package/es/v5_test.js +392 -1
  21. package/es/v5_test.js.map +1 -1
  22. package/es/v5_types.d.ts +15 -15
  23. package/es/v6.d.ts +18 -0
  24. package/es/v6.js +110 -0
  25. package/es/v6.js.map +1 -0
  26. package/helpers.d.ts +20 -2
  27. package/helpers.js +174 -1
  28. package/helpers.js.map +1 -1
  29. package/impl.d.ts +32 -20
  30. package/impl.js +1007 -1
  31. package/impl.js.map +1 -1
  32. package/index.d.ts +31 -41
  33. package/index.js +1057 -1
  34. package/index.js.map +1 -1
  35. package/middlewares.d.ts +1 -1
  36. package/middlewares.js +526 -1
  37. package/middlewares.js.map +1 -1
  38. package/package.json +4 -2
  39. package/test.d.ts +10 -8
  40. package/test.js +348 -1
  41. package/test.js.map +1 -1
  42. package/types.d.ts +66 -50
  43. package/v5.js +322 -1
  44. package/v5.js.map +1 -1
  45. package/v5_test.js +416 -1
  46. package/v5_test.js.map +1 -1
  47. package/v5_types.d.ts +15 -15
  48. package/v6.d.ts +18 -0
  49. package/v6.js +135 -0
  50. package/v6.js.map +1 -0
  51. package/dist/impl.d.ts +0 -195
  52. package/dist/impl.js +0 -2
  53. package/dist/impl.js.map +0 -1
  54. package/dist/index.d.ts +0 -168
  55. package/dist/index.js +0 -2
  56. package/dist/index.js.map +0 -1
  57. package/dist/middlewares.d.ts +0 -26
  58. package/dist/middlewares.js +0 -2
  59. package/dist/middlewares.js.map +0 -1
  60. package/dist/shallow.d.ts +0 -15
  61. package/dist/test.d.ts +0 -134
  62. package/dist/test.js +0 -2
  63. package/dist/test.js.map +0 -1
  64. package/dist/types.d.ts +0 -352
  65. package/dist/v5.d.ts +0 -58
  66. package/dist/v5.js +0 -2
  67. package/dist/v5.js.map +0 -1
  68. package/dist/v5_test.d.ts +0 -70
  69. package/dist/v5_test.js +0 -2
  70. package/dist/v5_test.js.map +0 -1
  71. package/dist/v5_types.d.ts +0 -227
package/README.md CHANGED
@@ -58,8 +58,7 @@ yarn add state-decorator
58
58
  ### Example
59
59
 
60
60
  ```typescript
61
- import React from 'react';
62
- import { useLocalStore, StoreActions } from 'state-decorator';
61
+ import { useLocalStore, StoreConfig } from "state-decorator";
63
62
 
64
63
  // Typings
65
64
 
@@ -84,45 +83,48 @@ type Props = {
84
83
  };
85
84
 
86
85
  // Initial state & actions
87
-
88
- export const getInitialState = (): State => ({
89
- selectedUserId: null,
90
- user: null,
91
- });
92
-
93
- export const userAppActions: StoreActions<State, Actions, Props> = {
94
- // synchronous action (simple form)
95
- selectUser: ({ args: [id] }) => ({ selectedUserId: id }),
96
-
97
- // a synchronous action with side effects
98
- selectAndLoadUser: {
99
- // effects contain only immutable state changes
100
- effects: ({ args: [id] }) => ({ selectedUserId: id }),
101
- // side effects (ie. other effects than state changes), using "actions" alias
102
- sideEffects: ({ a }) => {
103
- a.loadUser();
86
+ const config: StoreConfig<State, Actions, Props> = {
87
+ getInitialState: () => ({
88
+ selectedUserId: null,
89
+ user: null,
90
+ }),
91
+
92
+ actions: {
93
+ // synchronous action (simple form)
94
+ selectUser: ({ args: [id] }) => ({ selectedUserId: id }),
95
+
96
+ // a synchronous action with side effects
97
+ selectAndLoadUser: {
98
+ // effects contain only immutable state changes
99
+ effects: ({ args: [id] }) => ({ selectedUserId: id }),
100
+ // side effects (ie. other effects than state changes), using "actions" alias
101
+ sideEffects: ({ a }) => {
102
+ a.loadUser();
103
+ },
104
104
  },
105
- },
106
105
 
107
- // asynchronous action
108
- loadUser: {
109
- // promise provider, can use action context (ctx) to get current state, args, props etc.
110
- getPromise: (ctx) => Promise.resolve({ firstName: 'John', lastName: 'Doe' }),
111
- // state changes
112
- effects: ({ res }) => ({ user: res }),
113
- // state changes if error
114
- errorEffects: ({ error }) => ({ user: null }),
115
- // side effects here
116
- sideEffects: ({ res, actions }) => {},
117
- // side effects if error
118
- errorSideEffects: ({ err, actions }) => {},
119
- // many more options available here, read the doc!
106
+ // asynchronous action
107
+ loadUser: {
108
+ // promise provider, can use action context (ctx) to get current state, args, props etc.
109
+ getPromise: (ctx) =>
110
+ Promise.resolve({ firstName: "John", lastName: "Doe" }),
111
+ // state changes
112
+ effects: ({ res }) => ({ user: res }),
113
+ // state changes if error
114
+ errorEffects: ({ error }) => ({ user: null }),
115
+ // side effects here
116
+ sideEffects: ({ res, actions }) => {},
117
+ // side effects if error
118
+ errorSideEffects: ({ err, actions }) => {},
119
+ // many more options available here, read the doc!
120
+ },
120
121
  },
121
122
  };
122
123
 
124
+
123
125
  // Bind to react component and use state/actions
124
126
  export function App(props: Props) => {
125
- const { state, actions } = useLocalStore(getInitialState, userAppActions, props);
127
+ const { state, actions } = useLocalStore(config);
126
128
  return (
127
129
  <div>
128
130
  {state.user && (
@@ -160,12 +162,13 @@ export function App(props: Props) => {
160
162
  The initial state is build using a function provided to the store.
161
163
 
162
164
  ```typescript
163
- export const getInitialState = (p: Props): State => ({
164
- counter: 0,
165
- });
165
+ const config: StoreConfig<State, Actions, Props> = {
166
+ getInitialState: (p) => ({ counter: 0 }),
167
+ actions: {},
168
+ };
166
169
 
167
170
  export const CounterContainer = () => {
168
- const { state, actions } = useLocalStore(getInitialState, actionsImpl);
171
+ const { state, actions } = useLocalStore(config);
169
172
  return <CounterView {...state} {...actions} />;
170
173
  };
171
174
  ```
@@ -185,6 +188,7 @@ The action context contain all the data needed to implement the actions. It cont
185
188
  | derived | ds | everywhere | The derived state (from props/state) |
186
189
  | promiseId | | asynchronous actions callbacks | Parallel asynchronous actions promise identifier |
187
190
  | notifyWarning | | side effects | Injected function if set at store or global level |
191
+ | clearError | | side effects | Clear an error from ErrorMap |
188
192
  | indices | | onPropsChange | Indices in the getDeps array of changed props |
189
193
  | isInit | | onPropsChange | Whether the onPropsCHange is executed during init phase |
190
194
 
@@ -205,8 +209,10 @@ action: (ctx) => ctx.state, // changes to be merged to current state
205
209
  Example:
206
210
 
207
211
  ```typescript
208
- const actions: StoreActions<State, Actions> = {
209
- setText: ({ args: [newText] }) => ({ text: newText }),
212
+ const config: StoreConfig<State, Actions, Props> = {
213
+ actions: {
214
+ setText: ({ args: [newText] }) => ({ text: newText }),
215
+ },
210
216
  };
211
217
  ```
212
218
 
@@ -229,14 +235,16 @@ action: {
229
235
  Example:
230
236
 
231
237
  ```typescript
232
- const actions: StoreActions<State, Actions> = {
233
- setText: {
234
- effects: ({ args: [newText] }) => ({ text: newText }),
235
- // use either debounceTimeout or debounceSideEffectsTimeout
236
- // debounceTimeout: 5000,
237
- debounceSideEffectsTimeout: 5000,
238
- sideEffects: ({ a }) => {
239
- a.saveDocument();
238
+ const config: StoreConfig<State, Actions, Props> = {
239
+ actions: {
240
+ setText: {
241
+ effects: ({ args: [newText] }) => ({ text: newText }),
242
+ // use either debounceTimeout or debounceSideEffectsTimeout
243
+ // debounceTimeout: 5000,
244
+ debounceSideEffectsTimeout: 5000,
245
+ sideEffects: ({ a }) => {
246
+ a.saveDocument();
247
+ },
240
248
  },
241
249
  },
242
250
  };
@@ -278,14 +286,16 @@ action: {
278
286
  Example:
279
287
 
280
288
  ```typescript
281
- const actions: StoreActions<State, Actions> = {
282
- loadList: {
283
- getPromise: () => fetch('https://myapi/myservices').then((res) => (res.ok ? res.json() : Promise.reject())),
284
- effects: ({ res }) => ({ list: res }),
285
- sideEffects: ({ a }) => {
286
- a.otherAction();
289
+ const config: StoreConfig<State, Actions, Props> = {
290
+ actions: {
291
+ loadList: {
292
+ getPromise: () => fetch('https://myapi/myservices').then((res) => (res.ok ? res.json() : Promise.reject())),
293
+ effects: ({ res }) => ({ list: res }),
294
+ sideEffects: ({ a }) => {
295
+ a.otherAction();
296
+ },
297
+ getErrorMessage: () => 'Cannot load list',
287
298
  },
288
- getErrorMessage: () => 'Cannot load list',
289
299
  },
290
300
  };
291
301
  ```
@@ -383,11 +393,13 @@ const store = useLocalStore(getInitialState, actionsImpl, props, {
383
393
 
384
394
  ```typescript
385
395
  // action implementation
386
- const actions: StoreActions<State, Actions> = {
387
- loadList: {
388
- getPromise: () => new Promise((_, reject) => setTimeout(reject, 500, new Error('Too bad'))),
389
- getSuccessMessage: () => 'success!',
390
- getErrorMessage: ({ err }) => `Error: ${err.message}`,
396
+ const config: StoreConfig<State, Actions, Props> = {
397
+ actions: {
398
+ loadList: {
399
+ getPromise: () => new Promise((_, reject) => setTimeout(reject, 500, new Error('Too bad'))),
400
+ getSuccessMessage: () => 'success!',
401
+ getErrorMessage: ({ err }) => `Error: ${err.message}`,
402
+ },
391
403
  },
392
404
  };
393
405
  ```
@@ -448,6 +460,8 @@ if (errorMap.action1 instanceof MyCustomError){
448
460
 
449
461
  Recipe: When only using the **errorMap** to manage errors (ie. there's no effects nor message and so on), the error is considered _not managed_ resulting in a rejected promise and global **asyncErrorHandler** called with _managed_ flag set to false. In that case, set **isErrorManaged** to `true` to specify that the error is _managed_ externally.
450
462
 
463
+ The error map is updated when the action is called again, or call **clearError** in a side effect.
464
+
451
465
  ## <a name="ConflictingActions"></a>Conflicting actions
452
466
 
453
467
  The StateDecorator is managing the asynchronous action calls one at a time (default) or in parallel.
@@ -502,10 +516,20 @@ setGlobalConfig({
502
516
  On a specific store:
503
517
 
504
518
  ```typescript
505
- import { createStore } from 'state-decorator';
519
+ import { createStore, StoreConfig } from 'state-decorator';
506
520
  import { optimisticActions } from 'state-decorator/middlewares';
507
521
 
508
- const store = createStore(getInitialState, actionsImpl, options, [optimisticActions()],
522
+ const config: StoreConfig<State, Actions, Props> = {
523
+ getInitialState: () => ({
524
+ /* ... */
525
+ }),
526
+ actions: {
527
+ /* ... */
528
+ },
529
+ middlewares: [optimisticActions()],
530
+ };
531
+
532
+ const store = createStore(config);
509
533
  ```
510
534
 
511
535
  An optimistic action assumes that the action will, most of the time, succeed. So it will apply the effects as soon as the asynchronous action is called (as opposite to the regular effects which are applied when the promise is resolved).
@@ -523,11 +547,13 @@ The undo strategy is the following:
523
547
  Example:
524
548
 
525
549
  ```typescript
526
- const actions: StoreActions<State, Actions> = {
527
- deleteItem: {
528
- getPromise: ([id]) => new Promise((resolve) => setTimeout(resolve, 500)),
529
- optimisticEffects: ({ args: [id] }) => ({ list: list.filter((item) => item.id === id) }),
530
- errorEffects: ({err} => ({ err: error.message }),
550
+ const config: StoreConfig<State, Actions, Props> = {
551
+ actions: {
552
+ deleteItem: {
553
+ getPromise: ([id]) => new Promise((resolve) => setTimeout(resolve, 500)),
554
+ optimisticEffects: ({ args: [id] }) => ({ list: list.filter((item) => item.id === id) }),
555
+ errorEffects: ({err} => ({ err: error.message }),
556
+ },
531
557
  },
532
558
  };
533
559
  ```
@@ -551,26 +577,28 @@ Ongoing asynchronous actions can be aborted using, under the hood, the [AbortCon
551
577
  3. An abort is a specific failure of an action, ie. **errorEffects** and **errorSideEffects** will be called. Use the error type and name to distinguish an aborted action from a regular failed action.
552
578
 
553
579
  ```typescript
554
- export const actionsAbort: StoreActions<State, Actions, Props> = {
555
- onAction: {
556
- abortable: true,
557
- preEffects: () => ({ isError: false, isSuccess: false, isAborted: false }),
558
- getPromise: ({s, args: [willCrash] abortSignal}) =>
559
- new Promise((resolve, reject) => {
560
- const timeout = window.setTimeout(willCrash ? reject : resolve, 2500, willCrash ? new Error('boom') : 'result');
561
- abortSignal.addEventListener('abort', () => {
562
- window.clearTimeout(timeout);
563
- reject(new DOMException('Aborted', 'AbortError'));
564
- });
565
- }),
566
- errorEffects: ({ err}) => (err.name === 'AbortError' ? { isAborted: true } : { isError: true }),
567
- effects: () => ({ isSuccess: true }),
568
- errorSideEffects: ({ err }) => {
569
- if (e.name === 'AbortError') {
570
- console.log('AbortError side effect');
571
- } else {
572
- console.log('Other error side effect');
573
- }
580
+ const config: StoreConfig<State, Actions, Props> = {
581
+ actions: {
582
+ onAction: {
583
+ abortable: true,
584
+ preEffects: () => ({ isError: false, isSuccess: false, isAborted: false }),
585
+ getPromise: ({s, args: [willCrash] abortSignal}) =>
586
+ new Promise((resolve, reject) => {
587
+ const timeout = window.setTimeout(willCrash ? reject : resolve, 2500, willCrash ? new Error('boom') : 'result');
588
+ abortSignal.addEventListener('abort', () => {
589
+ window.clearTimeout(timeout);
590
+ reject(new DOMException('Aborted', 'AbortError'));
591
+ });
592
+ }),
593
+ errorEffects: ({ err}) => (err.name === 'AbortError' ? { isAborted: true } : { isError: true }),
594
+ effects: () => ({ isSuccess: true }),
595
+ errorSideEffects: ({ err }) => {
596
+ if (e.name === 'AbortError') {
597
+ console.log('AbortError side effect');
598
+ } else {
599
+ console.log('Other error side effect');
600
+ }
601
+ },
574
602
  },
575
603
  },
576
604
  };
@@ -604,28 +632,32 @@ const actions: StoreActions<State, Actions> = {
604
632
  };
605
633
  ```
606
634
 
607
- | Name | Purpose | Action signature |
608
- | ------------ | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
609
- | setArgIn | Effect function to update a state property from action first argument | (v:T) => void |
610
- | setTrueIn | Effect function to update a boolean state property to true | () => void |
611
- | setFalseIn | Effect function to update a boolean state property to false | () => void |
612
- | toggleProp | Effect function to toggle a boolean state property | () => void |
613
- | setMapProp | Effect function to update a state property of type Record<string, T> using first action argument as key and second as value | (id: string, v:T) => void |
614
- | setArrayItem | Effect function to update a state property of type T[] using first action argument as index and second as value | (index: number, v:T) => void |
615
- | setResIn | Effect function of asynchronous action to update a state property with the result of the promise | () => Promise<T> |
635
+ | Name | Purpose | Action signatue |
636
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
637
+ | setArgIn | Effect function to update a state property from action first argument | (v:T) => void |
638
+ | setTrueIn | Effect function to update a boolean state property to true | () => void |
639
+ | setFalseIn | Effect function to update a boolean state property to false | () => void |
640
+ | toggleProp | Effect function to toggle a boolean state property | () => void |
641
+ | setArgsInMap | Effect function to update a state property of type Record<string, T> using first action argument as key and second as value | (id: string, v:T) => void |
642
+ | setArgsInArray | Effect function to update a state property of type T[] using first action argument as index and second as value | (index: number, v:T) => void |
643
+ | setResIn | Effect function of asynchronous action to update a state property with the result of the promise | () => Promise<T> |
644
+ | setResInArray | Effect function to update a state property of type T[] using index from first action argument as index and as value the result of the promise | (index: number) => Promise <T> |
645
+ | setResInMap | Effect function to update a state property of type Record<string, T> using key from first action argument as index and as value the result of the promise | (id: string) => Promise <T> |
616
646
 
617
647
  ## Call actions on mount
618
648
 
619
649
  When store is created (for example whe React component in mounted), the **onMount** option is called.
620
650
 
621
651
  ```typescript
622
- import { createStore } from 'state-decorator';
652
+ import { StoreConfig } from 'state-decorator';
623
653
 
624
- const store = createStore(getInitialState, actionsImpl, {
625
- onMount: ({ a }) => {
626
- a.loadList();
654
+ const config: StoreConfig<State, Actions, Props> = {
655
+ getInitialState,
656
+ actions,
657
+ onMount: ({ p }) => {
658
+ p.loadParentList();
627
659
  },
628
- });
660
+ };
629
661
  ```
630
662
 
631
663
  After React component is mounted using _useLocalStore_, the **onMountDeferred** option is called (in a _useEffect_).
@@ -633,13 +665,15 @@ The **onMount** is called _during_ the render of the hooks. If the onMount code
633
665
  React will not update the parent state and will issue a warning.
634
666
 
635
667
  ```typescript
636
- import { createStore } from 'state-decorator';
668
+ import { StoreConfig } from 'state-decorator';
637
669
 
638
- const store = createStore(getInitialState, actionsImpl, {
670
+ const config: StoreConfig<State, Actions, Props> = {
671
+ getInitialState,
672
+ actions,
639
673
  onMountDeferred: ({ p }) => {
640
674
  p.loadParentList();
641
675
  },
642
- });
676
+ };
643
677
  ```
644
678
 
645
679
  If an initial action is launched each time a property changes, consider using the **onMount** flag of the **onPropsChange** entry.
@@ -649,14 +683,16 @@ If an initial action is launched each time a property changes, consider using th
649
683
  When the store is destroyed (for example on React component unmounted), the **onUnmount** option is called.
650
684
 
651
685
  ```typescript
652
- import { createStore } from 'state-decorator';
686
+ import { StoreConfig } from 'state-decorator';
653
687
 
654
- const store = createStore(getInitialState, actionsImpl, {
688
+ const config: StoreConfig<State, Actions, Props> = {
689
+ getInitialState,
690
+ actions,
655
691
  onUnmount: ({ s, abortedActions }) => {
656
692
  // ex: store s in local storage
657
693
  // ex: clean cache of aborted actions
658
694
  },
659
- });
695
+ };
660
696
  ```
661
697
 
662
698
  ## State sharing and slices
@@ -673,7 +709,7 @@ import { useStore, useStoreSlice, StoreActions } from 'state-decorator';
673
709
 
674
710
  // Create a store
675
711
 
676
- export const store = createStore(getInitialState, userAppActions);
712
+ export const store = createStore(config);
677
713
 
678
714
  // Bind to react component
679
715
 
@@ -710,7 +746,9 @@ export function SubComponent() {
710
746
  ```typescript
711
747
  import { createStore } from 'state-decorator';
712
748
 
713
- const store = createStore(getInitialState, actionsImpl, {
749
+ const config: StoreConfig<State, Actions, Props> = {
750
+ getInitialState,
751
+ actions,
714
752
  onPropsChange: {
715
753
  // list of depedencies that should trigger effects & side effects if changed
716
754
  getDeps: (p) => [p.id],
@@ -723,7 +761,7 @@ const store = createStore(getInitialState, actionsImpl, {
723
761
  // whether apply effects & side effects on mount
724
762
  onMount: true,
725
763
  },
726
- });
764
+ };
727
765
  ```
728
766
 
729
767
  ## Multiple props change configuration
@@ -735,9 +773,11 @@ The store can manage several props change configurations to have a better granul
735
773
  - Side effects are invoked after all effects using computed state
736
774
 
737
775
  ```typescript
738
- import { createStore } from 'state-decorator';
776
+ import { StoreConfig } from 'state-decorator';
739
777
 
740
- const store = createStore(getInitialState, actionsImpl, {
778
+ const config: StoreConfig<State, Actions, Props> = {
779
+ getInitialState,
780
+ actions,
741
781
  onPropsChange: [
742
782
  {
743
783
  getDeps: (p) => [p.id],
@@ -757,7 +797,7 @@ const store = createStore(getInitialState, actionsImpl, {
757
797
  },
758
798
  },
759
799
  ],
760
- });
800
+ };
761
801
  ```
762
802
 
763
803
  ## OnMount
@@ -767,7 +807,11 @@ If the onMount flag is set on the props change configuration, the effects and si
767
807
  It allows to trigger same actions at creation time and when the dependencies props are changed.
768
808
 
769
809
  ```typescript
770
- const store = createStore(getInitialState, actionsImpl, {
810
+ import { StoreConfig } from 'state-decorator';
811
+
812
+ const config: StoreConfig<State, Actions, Props> = {
813
+ getInitialState,
814
+ actions,
771
815
  onMount: ({ p }) => {
772
816
  a.loadUser(p.userId);
773
817
  },
@@ -780,11 +824,15 @@ const store = createStore(getInitialState, actionsImpl, {
780
824
  },
781
825
  },
782
826
  ],
783
- });
827
+ };
784
828
 
785
829
  // SAME AS
786
830
 
787
- const store = createStore(getInitialState, actionsImpl, {
831
+ import { StoreConfig } from 'state-decorator';
832
+
833
+ const config: StoreConfig<State, Actions, Props> = {
834
+ getInitialState,
835
+ actions,
788
836
  onPropsChange: [
789
837
  {
790
838
  getDeps: (p) => [p.userId],
@@ -795,7 +843,7 @@ const store = createStore(getInitialState, actionsImpl, {
795
843
  onMount: true,
796
844
  },
797
845
  ],
798
- });
846
+ };
799
847
  ```
800
848
 
801
849
  **Caution**: the effects are executed too. Use the isInit parameter is passed to the context to apply or not state change.
@@ -881,7 +929,7 @@ import { useLocalStore } from 'state-decorator';
881
929
  // Create and bind to react component
882
930
  // Each time the store state changes, a re-render is done.
883
931
  export function Container(props: Props) {
884
- const { state, actions } = useLocalStore(getInitialState, userAppActions, props);
932
+ const { state, actions } = useLocalStore(config, props);
885
933
  return (
886
934
  <div>
887
935
  <Title text={state.title} />
@@ -969,7 +1017,7 @@ export const StoreContext = createContext<StoreContextProps>(null);
969
1017
  // But this context is NOT refreshed if the store state is changed
970
1018
  export function StoreContextProvider(p: { children: any; propIn: string }) {
971
1019
  const { children, ...props } = p;
972
- const store = useLocalStore(getInitialState, actionsImpl, props);
1020
+ const store = useLocalStore(config, props);
973
1021
  return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
974
1022
  }
975
1023
 
@@ -1032,23 +1080,21 @@ type DerivedState = {
1032
1080
  derivedProp: number;
1033
1081
  };
1034
1082
 
1035
- const store = createStore<State, Actions, Props, DerivedState>(
1036
- () => ({ value: 0 }),
1037
- {
1083
+ const store = createStore<State, Actions, Props, DerivedState>({
1084
+ getInitialState: () => ({ value: 0 }),
1085
+ actions: {
1038
1086
  add1: ({ s }) => ({ ...s, value: s.value + 1 }),
1039
1087
  },
1040
- {
1041
- derivedState: {
1042
- derivedProp: {
1043
- // get the list of dependencies to be checked to trigger the computation of the derived state
1044
- // if state.value AND/OR props.prop1 changes, derivedProp is recomputed
1045
- getDeps: ({ state, props }) => [state.value, props.propIn],
1046
- // compute derived state from state & props (use short aliases)
1047
- get: ({ s, p }) => s.value * p.propIn,
1048
- },
1088
+ derivedState: {
1089
+ derivedProp: {
1090
+ // get the list of dependencies to be checked to trigger the computation of the derived state
1091
+ // if state.value AND/OR props.prop1 changes, derivedProp is recomputed
1092
+ getDeps: ({ state, props }) => [state.value, props.propIn],
1093
+ // compute derived state from state & props (use short aliases)
1094
+ get: ({ s, p }) => s.value * p.propIn,
1049
1095
  },
1050
- }
1051
- );
1096
+ },
1097
+ });
1052
1098
 
1053
1099
  export function App(props: Props) {
1054
1100
  const { state } = useStore(store, props);
@@ -1100,7 +1146,7 @@ To debug actions, add a middleware to the store.
1100
1146
  import { logEffects } from 'state-decorator/middlewares';
1101
1147
 
1102
1148
  function Container(props: Props) {
1103
- const { state, actions } = useLocalStore(getInitialState, actionsImpl, props, { name: 'My Store' }, [logEffects()]);
1149
+ const { state, actions } = useLocalStore({ getInitialState, actions, middlewares: [logEffects()] });
1104
1150
  return <div />;
1105
1151
  }
1106
1152
  ```
@@ -1114,7 +1160,7 @@ function logger(...args: any[]) {
1114
1160
 
1115
1161
  const logMiddleware = logEffects(logger);
1116
1162
 
1117
- const store = createStore(getInitialState, actionsImpl, props, options, [logMiddleware]);
1163
+ const store = createStore({ getInitialState, actions, middlewares: [logMiddleware] });
1118
1164
  ```
1119
1165
 
1120
1166
  Example:
@@ -1130,9 +1176,7 @@ Example:
1130
1176
  import { logDetailedEffects } from 'state-decorator/middlewares';
1131
1177
 
1132
1178
  function Container(props: Props) {
1133
- const { state, actions } = useLocalStore(getInitialState, actionsImpl, props, { name: 'My Store' }, [
1134
- logDetailedEffects(),
1135
- ]);
1179
+ const { state, actions } = useLocalStore({ getInitialState, actions, middlewares: [logDetailedEffects()] });
1136
1180
  return <div />;
1137
1181
  }
1138
1182
  ```
@@ -1161,7 +1205,7 @@ You can connect any StateDecorator store to [Chrome Redux devtools extension](ht
1161
1205
  import { devtools } from 'state-decorator/middlewares';
1162
1206
 
1163
1207
  function Container(props: Props) {
1164
- const { state, actions } = useLocalStore(getInitialState, actionsImpl, props, { name: 'My Store' }, [devtools()]);
1208
+ const { state, actions } = useLocalStore({ getInitialState, actions, middlewares: [devtools()] });
1165
1209
  return <div />;
1166
1210
  }
1167
1211
  ```
@@ -1203,7 +1247,8 @@ describe('Todo', () => {
1203
1247
  // this store can be shared accross tests because it is immutable
1204
1248
  const store = createMockFromStore(todoStore)
1205
1249
  // or
1206
- // const store = createMockStore(getInitialState, actions, props, options)
1250
+ // const store = createMockStore(storeConfig, props)
1251
+
1207
1252
  // initial state / props can be set in full form or partial form
1208
1253
  .setPartialState({
1209
1254
  idCount: 3,
@@ -1626,6 +1671,12 @@ TypeError: Cannot set property 'crash' of null
1626
1671
 
1627
1672
  ## API
1628
1673
 
1674
+ ### Create mock store
1675
+
1676
+ | Name | Parameters | |
1677
+ | --------------- | ------------------------------------------------ | --------------------------------- |
1678
+ | createMockStore | (config: StoreConfig, props: Props) => MockStore | Create a mock store from a config |
1679
+
1629
1680
  ### MockStore
1630
1681
 
1631
1682
  | Function | Arguments | Returns | Description |
@@ -1675,7 +1726,65 @@ TypeError: Cannot set property 'crash' of null
1675
1726
 
1676
1727
  # Migration
1677
1728
 
1678
- ## Step 1: v5 compatibility layer
1729
+ ## V6 to V7
1730
+
1731
+ ### Step 1: V6 compatibility layer
1732
+
1733
+ ```diff
1734
+ -import useLocalStore from 'state-decorator';
1735
+ -import { useLocalStore } from 'state-decorator';
1736
+
1737
+ +import { useLocalStoreV6 } from 'state-decorator';
1738
+ ```
1739
+
1740
+ ### Step 2: Migrate code
1741
+
1742
+ #### Config
1743
+
1744
+ ```diff
1745
+ - const getInitialState: (p:Props) => ({myProp: ''});
1746
+ -
1747
+ - const actions: StoreActions<State, Actions, Props> = {
1748
+ - myAction: ({s, args: [v]}) => ({ ...s, myProp: v })
1749
+ -}
1750
+ -
1751
+ - const options: StoreOptions<State, Action, Props> = {
1752
+ - onMount: () => {},
1753
+ - onPropsChange: {
1754
+ - getDeps: () => [],
1755
+ - effects: ({s, p}) => ({ ...s, myProp: p.prop }),
1756
+ - }
1757
+ - };
1758
+ -
1759
+ - function MyComponent(p:Props) {
1760
+ - const {state, actions} = useLocalStore(getInitialState, actions, props, options, [logEffects()]);
1761
+ - return <div>{myProp}</div>;
1762
+ - }
1763
+
1764
+ + const config:StoreConfig<State, Actions, Props> = {
1765
+ + getInitialState: () => ({ myProp: '' }),
1766
+ + actions: {
1767
+ + myAction: ({args: [v]}) => ({ myProp: v})
1768
+ + // or
1769
+ + myAction: setArgIn('myProp'),
1770
+ + },
1771
+ + onMount: () => {},
1772
+ + onPropsChange: {
1773
+ + getDeps: () => [],
1774
+ + effects: ({ p }) => ({ myProp: p.prop })
1775
+ + },
1776
+ + middlewares: [logEffects()],
1777
+ + }
1778
+ +
1779
+ + function MyComponent(p:Props) {
1780
+ + const {state, actions} = useLocalStore(config);
1781
+ + return <div>{myProp}</div>;
1782
+ + }
1783
+ ```
1784
+
1785
+ ## V5 to V6
1786
+
1787
+ ### Step 1: v5 compatibility layer
1679
1788
 
1680
1789
  To use the new engine while still using v5 API, juste import the v5 compatibility layer.
1681
1790
  You can new store in new code.
@@ -1696,26 +1805,26 @@ setGlobalConfig({
1696
1805
  });
1697
1806
  ```
1698
1807
 
1699
- ## Step 2: Migrate code but keep v5 tests
1808
+ ### Step 2: Migrate code but keep v5 tests
1700
1809
 
1701
- ### Code
1810
+ #### Code
1702
1811
 
1703
- #### Types
1812
+ ##### Types
1704
1813
 
1705
1814
  | Before | After |
1706
1815
  | --------------------- | ------------ |
1707
1816
  | StateDecoratorActions | StoreActions |
1708
1817
  | StateDecoratorOptions | StoreOptions |
1709
1818
 
1710
- #### Optimistic actions
1819
+ ##### Optimistic actions
1711
1820
 
1712
1821
  If you are using optimistic effects, make sure to set the **optimisticActions** middleware to your stores, see [Optimistic actions](#optimistic-actions).
1713
1822
 
1714
- ### Hooks
1823
+ #### Hooks
1715
1824
 
1716
1825
  Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1717
1826
 
1718
- ### Actions
1827
+ #### Actions
1719
1828
 
1720
1829
  - rename action properties
1721
1830
 
@@ -1756,9 +1865,9 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1756
1865
  };
1757
1866
  ```
1758
1867
 
1759
- ### Options
1868
+ #### Options
1760
1869
 
1761
- #### onMount
1870
+ ##### onMount
1762
1871
 
1763
1872
  ```diff
1764
1873
  - const options: StateDecoratorOptions<S, A, P> = {
@@ -1770,7 +1879,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1770
1879
  +} ;
1771
1880
  ```
1772
1881
 
1773
- #### logEnabled
1882
+ ##### logEnabled
1774
1883
 
1775
1884
  ```diff
1776
1885
  - function Container(props: Props) {
@@ -1786,7 +1895,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1786
1895
  + }
1787
1896
  ```
1788
1897
 
1789
- #### Props changes
1898
+ ##### Props changes
1790
1899
 
1791
1900
  ```diff
1792
1901
  - const options: StateDecoratorOptions<S, A, P> = {
@@ -1808,7 +1917,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1808
1917
  +} ;
1809
1918
  ```
1810
1919
 
1811
- ### Reuse v5 tests with v6 actions
1920
+ #### Reuse v5 tests with v6 actions
1812
1921
 
1813
1922
  ```diff
1814
1923
  - import { testSyncAction, testAsyncAction } from 'state-decorator';
@@ -1816,7 +1925,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1816
1925
  + import { testV6SyncAction, testV6AsyncAction } from 'state-decorator/v5_test';
1817
1926
  ```
1818
1927
 
1819
- ## Step 3 (optional): Migrate tests
1928
+ ### Step 3 (optional): Migrate tests
1820
1929
 
1821
1930
  🎊 Congratulations! 🎊
1822
1931
 
@@ -1864,34 +1973,27 @@ You can add this snippet to quickly create a stateful functional component using
1864
1973
  "import React from 'react';",
1865
1974
  "import { useStore, StoreActions, LoadingProps } from 'state-decorator';",
1866
1975
  "",
1867
- "export type $1Props = {};",
1868
- "",
1869
- "export type $1State = {};",
1976
+ "export type Props = {};",
1870
1977
  "",
1871
- "export type $1Actions = {};",
1978
+ "export type State = {};",
1872
1979
  "",
1873
- "type Props = $1Props;",
1874
- "type State = $1State;",
1875
- "type Actions = $1Actions;",
1980
+ "export type Actions = {};",
1876
1981
  "",
1877
1982
  "type ViewProps = Props & Actions & State & Pick<LoadingProps<Actions>, 'loadingMap'>;",
1878
1983
  "",
1879
- "export function getInitialState(p:$1Props): State {",
1880
- "return {};",
1984
+ "export const storeConfig: StoreConfig<State, Actions, Props> = {",
1985
+ " getInitialState: (p:$1Props) => ({",
1986
+ " }),",
1987
+ " actions: {",
1988
+ " }",
1881
1989
  "};",
1882
1990
  "",
1883
1991
  "export function $1View(p: ViewProps) {",
1884
1992
  " return (<div>[$1View]</div>);",
1885
1993
  "}",
1886
1994
  "",
1887
- "export const actions$1: StoreActions<State, Actions, Props> = {};",
1888
- "",
1889
- "export function onMount(actions:$1Actions) {}",
1890
- "",
1891
- "const store = createStore(getInitialState, actions$1, { onMount });",
1892
- "",
1893
1995
  "export default React.memo(function $1(p: $1Props) {",
1894
- " const { state:s, actions:a, loadingMap } = useStore(store, p);",
1996
+ " const { state:s, actions:a, loadingMap } = useLocalStore(config, p);",
1895
1997
  " return <$1View {...p} {...s} {...a} loadingMap={loadingMap} />;",
1896
1998
  "});"
1897
1999
  ],