state-decorator 7.0.0-beta.1 → 7.0.0-beta.2

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 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
  ```
@@ -1675,7 +1719,65 @@ TypeError: Cannot set property 'crash' of null
1675
1719
 
1676
1720
  # Migration
1677
1721
 
1678
- ## Step 1: v5 compatibility layer
1722
+ ## V6 to V7
1723
+
1724
+ ### Step 1: V6 compatibility layer
1725
+
1726
+ ```diff
1727
+ -import useLocalStore from 'state-decorator';
1728
+ -import { useLocalStore } from 'state-decorator';
1729
+
1730
+ +import { useLocalStoreV6 } from 'state-decorator';
1731
+ ```
1732
+
1733
+ ### Step 2: Migrate code
1734
+
1735
+ #### Config
1736
+
1737
+ ```diff
1738
+ - const getInitialState: (p:Props) => ({myProp: ''});
1739
+ -
1740
+ - const actions: StoreActions<State, Actions, Props> = {
1741
+ - myAction: ({s, args: [v]}) => ({ ...s, myProp: v })
1742
+ -}
1743
+ -
1744
+ - const options: StoreOptions<State, Action, Props> = {
1745
+ - onMount: () => {},
1746
+ - onPropsChange: {
1747
+ - getDeps: () => [],
1748
+ - effects: ({s, p}) => ({ ...s, myProp: p.prop }),
1749
+ - }
1750
+ - };
1751
+ -
1752
+ - function MyComponent(p:Props) {
1753
+ - const {state, actions} = useLocalStore(getInitialState, actions, props, options, [logEffects()]);
1754
+ - return <div>{myProp}</div>;
1755
+ - }
1756
+
1757
+ + const config:StoreConfig<State, Actions, Props> = {
1758
+ + getInitialState: () => ({ myProp: '' }),
1759
+ + actions: {
1760
+ + myAction: ({args: [v]}) => ({ myProp: v})
1761
+ + // or
1762
+ + myAction: setArgIn('myProp'),
1763
+ + },
1764
+ + onMount: () => {},
1765
+ + onPropsChange: {
1766
+ + getDeps: () => [],
1767
+ + effects: ({ p }) => ({ myProp: p.prop })
1768
+ + },
1769
+ + middlewares: [logEffects()],
1770
+ + }
1771
+ +
1772
+ + function MyComponent(p:Props) {
1773
+ + const {state, actions} = useLocalStore(config);
1774
+ + return <div>{myProp}</div>;
1775
+ + }
1776
+ ```
1777
+
1778
+ ## V5 to V6
1779
+
1780
+ ### Step 1: v5 compatibility layer
1679
1781
 
1680
1782
  To use the new engine while still using v5 API, juste import the v5 compatibility layer.
1681
1783
  You can new store in new code.
@@ -1696,26 +1798,26 @@ setGlobalConfig({
1696
1798
  });
1697
1799
  ```
1698
1800
 
1699
- ## Step 2: Migrate code but keep v5 tests
1801
+ ### Step 2: Migrate code but keep v5 tests
1700
1802
 
1701
- ### Code
1803
+ #### Code
1702
1804
 
1703
- #### Types
1805
+ ##### Types
1704
1806
 
1705
1807
  | Before | After |
1706
1808
  | --------------------- | ------------ |
1707
1809
  | StateDecoratorActions | StoreActions |
1708
1810
  | StateDecoratorOptions | StoreOptions |
1709
1811
 
1710
- #### Optimistic actions
1812
+ ##### Optimistic actions
1711
1813
 
1712
1814
  If you are using optimistic effects, make sure to set the **optimisticActions** middleware to your stores, see [Optimistic actions](#optimistic-actions).
1713
1815
 
1714
- ### Hooks
1816
+ #### Hooks
1715
1817
 
1716
1818
  Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1717
1819
 
1718
- ### Actions
1820
+ #### Actions
1719
1821
 
1720
1822
  - rename action properties
1721
1823
 
@@ -1756,9 +1858,9 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1756
1858
  };
1757
1859
  ```
1758
1860
 
1759
- ### Options
1861
+ #### Options
1760
1862
 
1761
- #### onMount
1863
+ ##### onMount
1762
1864
 
1763
1865
  ```diff
1764
1866
  - const options: StateDecoratorOptions<S, A, P> = {
@@ -1770,7 +1872,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1770
1872
  +} ;
1771
1873
  ```
1772
1874
 
1773
- #### logEnabled
1875
+ ##### logEnabled
1774
1876
 
1775
1877
  ```diff
1776
1878
  - function Container(props: Props) {
@@ -1786,7 +1888,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1786
1888
  + }
1787
1889
  ```
1788
1890
 
1789
- #### Props changes
1891
+ ##### Props changes
1790
1892
 
1791
1893
  ```diff
1792
1894
  - const options: StateDecoratorOptions<S, A, P> = {
@@ -1808,7 +1910,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1808
1910
  +} ;
1809
1911
  ```
1810
1912
 
1811
- ### Reuse v5 tests with v6 actions
1913
+ #### Reuse v5 tests with v6 actions
1812
1914
 
1813
1915
  ```diff
1814
1916
  - import { testSyncAction, testAsyncAction } from 'state-decorator';
@@ -1816,7 +1918,7 @@ Choose one of _useLocalStore_, _useStore_, _useBindStore_ or _useStoreSlice_.
1816
1918
  + import { testV6SyncAction, testV6AsyncAction } from 'state-decorator/v5_test';
1817
1919
  ```
1818
1920
 
1819
- ## Step 3 (optional): Migrate tests
1921
+ ### Step 3 (optional): Migrate tests
1820
1922
 
1821
1923
  🎊 Congratulations! 🎊
1822
1924
 
@@ -1864,34 +1966,27 @@ You can add this snippet to quickly create a stateful functional component using
1864
1966
  "import React from 'react';",
1865
1967
  "import { useStore, StoreActions, LoadingProps } from 'state-decorator';",
1866
1968
  "",
1867
- "export type $1Props = {};",
1868
- "",
1869
- "export type $1State = {};",
1969
+ "export type Props = {};",
1870
1970
  "",
1871
- "export type $1Actions = {};",
1971
+ "export type State = {};",
1872
1972
  "",
1873
- "type Props = $1Props;",
1874
- "type State = $1State;",
1875
- "type Actions = $1Actions;",
1973
+ "export type Actions = {};",
1876
1974
  "",
1877
1975
  "type ViewProps = Props & Actions & State & Pick<LoadingProps<Actions>, 'loadingMap'>;",
1878
1976
  "",
1879
- "export function getInitialState(p:$1Props): State {",
1880
- "return {};",
1977
+ "export const storeConfig: StoreConfig<State, Actions, Props> = {",
1978
+ " getInitialState: (p:$1Props) => ({",
1979
+ " }),",
1980
+ " actions: {",
1981
+ " }",
1881
1982
  "};",
1882
1983
  "",
1883
1984
  "export function $1View(p: ViewProps) {",
1884
1985
  " return (<div>[$1View]</div>);",
1885
1986
  "}",
1886
1987
  "",
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
1988
  "export default React.memo(function $1(p: $1Props) {",
1894
- " const { state:s, actions:a, loadingMap } = useStore(store, p);",
1989
+ " const { state:s, actions:a, loadingMap } = useLocalStore(config, p);",
1895
1990
  " return <$1View {...p} {...s} {...a} loadingMap={loadingMap} />;",
1896
1991
  "});"
1897
1992
  ],