@mic-rexjs/usecases-react 2.0.2 → 2.1.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.
Files changed (102) hide show
  1. package/dist/index.min.js +1 -1
  2. package/es/components/UseCaseFragment/index.d.ts +2 -2
  3. package/es/components/UseCaseFragment/types.d.ts +3 -3
  4. package/es/configs/defaultUseCaseContext/index.d.ts +2 -3
  5. package/es/configs/defaultUseCaseContext/types.d.ts +10 -12
  6. package/es/configs/useCaseContextReferenceMap/types.d.ts +5 -5
  7. package/es/enums/UseCaseArgumentTypes/index.d.ts +5 -0
  8. package/es/enums/UseCaseArgumentTypes/index.js +6 -0
  9. package/es/enums/UseCaseModeErrors/index.d.ts +4 -0
  10. package/es/enums/UseCaseModeErrors/index.js +5 -0
  11. package/es/enums/UseCaseStatuses/index.d.ts +10 -0
  12. package/es/enums/UseCaseStatuses/index.js +18 -0
  13. package/es/hooks/useArgumentTypes/index.d.ts +3 -0
  14. package/es/hooks/useArgumentTypes/index.js +19 -0
  15. package/es/hooks/useContextualItem/index.d.ts +3 -0
  16. package/es/hooks/useContextualItem/index.js +13 -0
  17. package/es/hooks/useContextualItem/types.d.ts +3 -0
  18. package/es/hooks/useEntity/index.d.ts +4 -0
  19. package/es/hooks/useEntity/index.js +49 -0
  20. package/es/hooks/useGlobalUseCase/index.js +2 -8
  21. package/es/hooks/useGlobalUseCase/index.test.js +6 -56
  22. package/es/hooks/useGlobalUseCase/types.d.ts +4 -11
  23. package/es/hooks/useMemoizedCall/index.d.ts +2 -0
  24. package/es/hooks/useMemoizedCall/index.js +15 -0
  25. package/es/hooks/useProvider/index.d.ts +4 -4
  26. package/es/hooks/useProvider/index.js +16 -17
  27. package/es/hooks/useSafeMode/index.d.ts +3 -0
  28. package/es/hooks/useSafeMode/index.js +18 -0
  29. package/es/hooks/useStatelessUseCase/index.js +2 -5
  30. package/es/hooks/useStatelessUseCase/index.test.js +4 -42
  31. package/es/hooks/useStatelessUseCase/types.d.ts +2 -4
  32. package/es/hooks/useUseCase/index.js +45 -75
  33. package/es/hooks/useUseCase/index.test.js +74 -20
  34. package/es/hooks/useUseCase/types.d.ts +40 -45
  35. package/es/hooks/useUseCaseContext/index.d.ts +5 -3
  36. package/es/hooks/useUseCaseContext/index.js +10 -3
  37. package/es/hooks/useUseCaseContext/index.test.js +39 -12
  38. package/es/hooks/useUseCaseStatuses/index.d.ts +6 -0
  39. package/es/hooks/useUseCaseStatuses/index.js +29 -0
  40. package/es/index.d.ts +1 -1
  41. package/es/methods/cacheCall/index.d.ts +2 -0
  42. package/es/methods/cacheCall/index.js +26 -0
  43. package/es/methods/cacheCall/types.d.ts +13 -0
  44. package/es/methods/cacheCalls/index.d.ts +2 -0
  45. package/es/methods/cacheCalls/index.js +8 -0
  46. package/es/methods/fillArguments/index.d.ts +5 -0
  47. package/es/methods/fillArguments/index.js +41 -0
  48. package/es/methods/fillArguments/types.d.ts +10 -0
  49. package/es/methods/fillArguments/types.js +1 -0
  50. package/es/methods/getRenderingEntity/index.d.ts +3 -0
  51. package/es/methods/getRenderingEntity/index.js +13 -0
  52. package/es/methods/initEntityReducers/index.d.ts +2 -3
  53. package/es/methods/initEntityReducers/index.js +2 -3
  54. package/es/methods/triggerCallbacks/index.d.ts +1 -1
  55. package/es/methods/triggerCallbacks/index.js +3 -3
  56. package/lib/enums/UseCaseArgumentTypes/index.js +13 -0
  57. package/lib/enums/UseCaseModeErrors/index.js +12 -0
  58. package/lib/enums/UseCaseStatuses/index.js +25 -0
  59. package/lib/hooks/useArgumentTypes/index.js +31 -0
  60. package/lib/hooks/useContextualItem/index.js +21 -0
  61. package/lib/hooks/useEntity/index.js +63 -0
  62. package/lib/hooks/useGlobalUseCase/index.js +2 -15
  63. package/lib/hooks/useGlobalUseCase/index.test.js +5 -57
  64. package/lib/hooks/useMemoizedCall/index.js +22 -0
  65. package/lib/hooks/useProvider/index.js +18 -17
  66. package/lib/hooks/useSafeMode/index.js +25 -0
  67. package/lib/hooks/useStatelessUseCase/index.js +2 -12
  68. package/lib/hooks/useStatelessUseCase/index.test.js +2 -43
  69. package/lib/hooks/useUseCase/index.js +55 -83
  70. package/lib/hooks/useUseCase/index.test.js +104 -57
  71. package/lib/hooks/useUseCaseContext/index.js +12 -3
  72. package/lib/hooks/useUseCaseContext/index.test.js +45 -19
  73. package/lib/hooks/useUseCaseStatuses/index.js +36 -0
  74. package/lib/methods/cacheCall/index.js +42 -0
  75. package/lib/methods/cacheCalls/index.js +20 -0
  76. package/lib/methods/fillArguments/index.js +31 -0
  77. package/lib/methods/fillArguments/types.js +5 -0
  78. package/lib/methods/getRenderingEntity/index.js +20 -0
  79. package/lib/methods/initEntityReducers/index.js +2 -3
  80. package/lib/methods/triggerCallbacks/index.js +3 -3
  81. package/package.json +3 -3
  82. package/es/hooks/useChangeCallbackCollection/index.d.ts +0 -3
  83. package/es/hooks/useChangeCallbackCollection/index.js +0 -29
  84. package/es/methods/cacheReducerCalls/index.d.ts +0 -3
  85. package/es/methods/cacheReducerCalls/index.js +0 -26
  86. package/es/methods/cacheReducerCalls/types.d.ts +0 -11
  87. package/es/methods/getRootEntity/index.d.ts +0 -3
  88. package/es/methods/getRootEntity/index.js +0 -10
  89. package/es/methods/initEntityReducers/types.d.ts +0 -3
  90. package/es/methods/removeItem/index.d.ts +0 -1
  91. package/es/methods/removeItem/index.js +0 -7
  92. package/es/methods/toMaxArguments/index.d.ts +0 -3
  93. package/es/methods/toMaxArguments/index.js +0 -28
  94. package/lib/hooks/useChangeCallbackCollection/index.js +0 -37
  95. package/lib/methods/cacheReducerCalls/index.js +0 -45
  96. package/lib/methods/getRootEntity/index.js +0 -17
  97. package/lib/methods/removeItem/index.js +0 -14
  98. package/lib/methods/toMaxArguments/index.js +0 -25
  99. /package/es/{methods/cacheReducerCalls → hooks/useContextualItem}/types.js +0 -0
  100. /package/es/methods/{initEntityReducers → cacheCall}/types.js +0 -0
  101. /package/lib/{methods/cacheReducerCalls → hooks/useContextualItem}/types.js +0 -0
  102. /package/lib/methods/{initEntityReducers → cacheCall}/types.js +0 -0
@@ -1,88 +1,58 @@
1
- import { useCreation, useMemoizedFn } from 'ahooks';
2
- import { useContext, useState } from 'react';
3
- import { useConstant } from '../useConstant';
1
+ import { useCreation } from 'ahooks';
2
+ import { useContext } from 'react';
4
3
  import { useConstantFn } from '../useConstantFn';
5
4
  import { useProvider } from '../useProvider';
6
- import { useCompareDeps } from '../useCompareDeps';
7
5
  import { initEntityReducers } from '../../methods/initEntityReducers';
8
- import { triggerCallbacks } from '../../methods/triggerCallbacks';
9
- import { getRootEntity } from '../../methods/getRootEntity';
10
6
  import { useUseCaseContext } from '../useUseCaseContext';
11
- import { defaultUseCaseContext } from '../../configs/defaultUseCaseContext';
12
7
  import { useIsRenderingRef } from '../useIsRenderingRef';
13
- import { cacheReducerCalls } from '../../methods/cacheReducerCalls';
14
- import { UseCaseModes } from '../../enums/UseCaseModes';
15
- import { toMaxArguments } from '../../methods/toMaxArguments';
16
- import { useChangeCallbackCollection } from '../useChangeCallbackCollection';
8
+ import { fillArguments } from '../../methods/fillArguments';
9
+ import { useArgumentTypes } from '../useArgumentTypes';
10
+ import { useUseCaseStatuses } from '../useUseCaseStatuses';
11
+ import { UseCaseStatuses } from '../../enums/UseCaseStatuses';
12
+ import { useEntity } from '../useEntity';
13
+ import { useContextualItem } from '../useContextualItem';
14
+ import { cacheCalls } from '../../methods/cacheCalls';
15
+ import { useSafeMode } from '../useSafeMode';
17
16
  export const useUseCase = ((...args) => {
18
- const [unsafeEntity, unsafeUsecase, unsafeMode, options, deps] = toMaxArguments(args);
19
- const [entityState, setEntityState] = useState(unsafeEntity);
20
- const usecase = useConstantFn(unsafeUsecase);
21
- const entityUseCase = usecase;
22
- const mode = useConstant(unsafeMode);
23
- const depsKey = useCompareDeps(deps);
17
+ const argumentTypes = useArgumentTypes(args);
18
+ const fullArguments = fillArguments(args, argumentTypes);
19
+ const [unsafeEntity, unsafeUsecase, unsafeMode, options, deps] = fullArguments;
20
+ const unkownUsecase = useConstantFn(unsafeUsecase);
21
+ const usecase = unkownUsecase;
22
+ const entityUseCase = unkownUsecase;
24
23
  const isRenderingRef = useIsRenderingRef();
25
- const isContextRoot = useConstant(typeof args[1] === 'function');
26
- const defaultContext = defaultUseCaseContext;
27
- const context = useUseCaseContext(entityUseCase, isContextRoot ? null : defaultContext);
28
- const getContextValue = useContext(context);
29
- const hasContextValueGetter = getContextValue !== null;
30
- const { entity: contextEntity, reducers: contextEntityReducers = null, changeCallbackCollection: contextChangeCallbackCollection = null, } = getContextValue?.() || {};
31
- // 是否需要初始化根 `redurcers`
32
- const isRoot = isContextRoot || !hasContextValueGetter;
33
- // 是否为上下文模式
34
- const isContextMode = isContextRoot || hasContextValueGetter;
35
- const changeCallbackCollection = useChangeCallbackCollection(isRoot, contextChangeCallbackCollection, options);
36
- const entity = useCreation(() => {
37
- if (!isContextRoot) {
38
- return contextEntity;
24
+ const mode = useSafeMode(unsafeMode, argumentTypes);
25
+ const context = useUseCaseContext(unkownUsecase, argumentTypes, mode);
26
+ const contextValue = useContext(context);
27
+ const entityContextValue = contextValue;
28
+ const statuses = useUseCaseStatuses(argumentTypes, mode, contextValue);
29
+ const { reducers: contextReducers = null } = contextValue || {};
30
+ const { store: contextStore = null } = entityContextValue || {};
31
+ const [entity, store] = useEntity(statuses, unsafeEntity, contextStore, options);
32
+ const reducers = useContextualItem(statuses, contextReducers, () => {
33
+ if ((statuses & UseCaseStatuses.EntityEnabled) !== UseCaseStatuses.EntityEnabled) {
34
+ return usecase(options);
39
35
  }
40
- return getRootEntity(entityState, unsafeEntity, mode);
41
- }, [isContextRoot, contextEntity, mode, entityState, unsafeEntity]);
42
- const onEntityChange = useMemoizedFn((newEntity, oldEntity) => {
43
- if ((mode & UseCaseModes.Stateless) !== UseCaseModes.Stateless) {
44
- setEntityState(newEntity);
45
- }
46
- triggerCallbacks(changeCallbackCollection, newEntity, oldEntity);
47
- });
48
- const createRootEntityReducers = useMemoizedFn(() => {
49
- const initOptions = options;
50
- return initEntityReducers(entity, entityUseCase, initOptions, onEntityChange);
51
- });
52
- const createReducers = useMemoizedFn(() => {
53
- return usecase(options);
54
- });
55
- const rootEntityReducers = useCreation(() => {
56
- void depsKey;
57
- if (!isContextRoot) {
58
- return null;
36
+ const hookOptions = options;
37
+ return initEntityReducers(entityUseCase, store, hookOptions);
38
+ }, deps);
39
+ const Provider = useProvider(statuses, context, store, reducers);
40
+ return useCreation(() => {
41
+ if ((statuses & UseCaseStatuses.ContextEnabled) !== UseCaseStatuses.ContextEnabled) {
42
+ return reducers;
59
43
  }
60
- return createRootEntityReducers();
61
- }, [isContextRoot, depsKey, createRootEntityReducers]);
62
- const entityReducers = useCreation(() => {
63
- return rootEntityReducers || contextEntityReducers || {};
64
- }, [rootEntityReducers, contextEntityReducers]);
65
- const reducers = useCreation(() => {
66
- void depsKey;
67
- if (isContextMode) {
68
- return {};
44
+ const rootEnabled = (statuses & UseCaseStatuses.RootEnabled) === UseCaseStatuses.RootEnabled;
45
+ if ((statuses & UseCaseStatuses.EntityEnabled) !== UseCaseStatuses.EntityEnabled) {
46
+ return rootEnabled ? [reducers, Provider] : reducers;
69
47
  }
70
- return createReducers();
71
- }, [isContextMode, depsKey, createReducers]);
72
- const Provider = useProvider(context, mode, entity, entityReducers, changeCallbackCollection);
73
- const createCoreCollection = useMemoizedFn((coreEntity, coreReducers) => {
74
- return [
75
- coreEntity,
76
- cacheReducerCalls(coreReducers, () => {
48
+ const cachedReducers = cacheCalls(reducers, {
49
+ onShouldCache() {
77
50
  return isRenderingRef.current;
78
- }),
79
- isContextRoot ? Provider : null,
80
- ];
81
- });
82
- return useCreation(() => {
83
- if (isContextMode) {
84
- return createCoreCollection(entity, entityReducers);
51
+ },
52
+ });
53
+ if (rootEnabled) {
54
+ return [entity, cachedReducers, Provider];
85
55
  }
86
- return reducers;
87
- }, [isContextMode, entity, entityReducers, reducers, createCoreCollection]);
56
+ return [entity, cachedReducers];
57
+ }, [statuses, entity, reducers, Provider, isRenderingRef]);
88
58
  });
@@ -6,6 +6,7 @@ import { useUseCase } from '.';
6
6
  import { useDeepCompareEffect, useMemoizedFn, useMount, useUpdate, useUpdateEffect } from 'ahooks';
7
7
  import { Fragment, useEffect, useRef, useState } from 'react';
8
8
  import { UseCaseModes } from '../../enums/UseCaseModes';
9
+ import { UseCaseModeErrors } from '../../enums/UseCaseModeErrors';
9
10
  const EXT_1 = '.png';
10
11
  const EXT_2 = '.jpg';
11
12
  const PATH_1 = `hello${EXT_1}`;
@@ -86,7 +87,7 @@ const Child = ({ textPrefix = '', onUpdate, onPathChange, onUndefinedEntity }) =
86
87
  useUpdateEffect(pathChange, [path, pathChange]);
87
88
  return (_jsxs("div", { children: [_jsxs("main", { children: ["child path: ", path, _jsx("br", {}), "child ext: ", ext] }), _jsx("footer", { children: _jsx("button", { onClick: onClick, children: textPrefix + CHILD_BUTTON_TEXT }) })] }));
88
89
  };
89
- const Parent = ({ children, mode, onUpdate, onSetPath }) => {
90
+ const Parent = ({ children, mode = UseCaseModes.Normal, onUpdate, onSetPath }) => {
90
91
  const [{ path, ext }, { setPath }, Provider] = useUseCase(defaultFile, fileUseCase, mode);
91
92
  const onClick = () => {
92
93
  setPath(onSetPath?.() || '');
@@ -124,17 +125,17 @@ beforeEach(() => {
124
125
  subtractionReducer.mockClear();
125
126
  });
126
127
  describe('useUseCase', () => {
127
- describe.each([void 0, UseCaseModes.Normal, UseCaseModes.Global])('[mode=%s]: `useUseCase` should work the same as `useRootCoreCollection`', (mode) => {
128
+ describe('`useUseCase` should work the same as `useRootCoreCollection`', () => {
128
129
  test('check `context.length`', () => {
129
130
  const { result } = renderHook(() => {
130
- return useUseCase(defaultFile, fileUseCase, mode);
131
+ return useUseCase(defaultFile, fileUseCase);
131
132
  });
132
133
  const { current: cores } = result;
133
134
  expect(cores).toHaveLength(3);
134
135
  });
135
136
  test('`entity` should equal `defaultFile`', () => {
136
137
  const { result } = renderHook(() => {
137
- return useUseCase(defaultFile, fileUseCase, mode);
138
+ return useUseCase(defaultFile, fileUseCase);
138
139
  });
139
140
  const { current: cores } = result;
140
141
  const [entity] = cores;
@@ -412,6 +413,28 @@ describe('useUseCase', () => {
412
413
  fireEvent.click(screen.getByText(updateButtonText));
413
414
  expect(addReducer).toHaveBeenCalledTimes(3);
414
415
  });
416
+ test.each([
417
+ {
418
+ mode: UseCaseModes.Normal,
419
+ toThrow: '',
420
+ },
421
+ {
422
+ mode: UseCaseModes.Global,
423
+ toThrow: UseCaseModeErrors.UnsupportedGlobal,
424
+ },
425
+ {
426
+ mode: UseCaseModes.Stateless,
427
+ toThrow: '',
428
+ },
429
+ ])('does not support global mode', ({ mode, toThrow }) => {
430
+ expect(() => {
431
+ renderHook(() => {
432
+ useUseCase(defaultFile, fileUseCase, mode);
433
+ });
434
+ // 如果上面测试成功,则手动 throw '',保证与 toThrow 的值一致
435
+ throw '';
436
+ }).toThrow(toThrow);
437
+ });
415
438
  test('stateless mode should not generate new reducers when `yeild entity`', () => {
416
439
  const onUpdate = jest.fn();
417
440
  const { result, rerender } = renderHook((file = defaultFile) => {
@@ -1186,22 +1209,6 @@ describe('useUseCase', () => {
1186
1209
  expect(onParentUpdate).toHaveBeenCalledTimes(0);
1187
1210
  expect(onChildUpdate).toHaveBeenCalledTimes(0);
1188
1211
  });
1189
- test('global mode should trigger Parent to update, but Child shoud not be updated', () => {
1190
- const onParentUpdate = jest.fn();
1191
- const onChildUpdate = jest.fn();
1192
- const onSetPath = jest.fn(() => {
1193
- return PATH_1;
1194
- });
1195
- render(_jsx("div", { children: _jsx(Parent, { mode: UseCaseModes.Global, onSetPath: onSetPath, onUpdate: onParentUpdate, children: _jsx(Child, { onUpdate: onChildUpdate }) }) }));
1196
- fireEvent.click(screen.getByText(PARENT_BUTTON_TEXT));
1197
- expect(onSetPath).toHaveBeenCalledTimes(1);
1198
- expect(onParentUpdate).toHaveBeenCalledTimes(1);
1199
- expect(onChildUpdate).toHaveBeenCalledTimes(0);
1200
- fireEvent.click(screen.getByText(CHILD_BUTTON_TEXT));
1201
- expect(onSetPath).toHaveBeenCalledTimes(1);
1202
- expect(onParentUpdate).toHaveBeenCalledTimes(2);
1203
- expect(onChildUpdate).toHaveBeenCalledTimes(0);
1204
- });
1205
1212
  test('`usecase` should not be called at child component', () => {
1206
1213
  const mockUseCase = jest.fn(fileUseCase);
1207
1214
  const A = () => {
@@ -1403,6 +1410,53 @@ describe('useUseCase', () => {
1403
1410
  expect(onChangeC).toHaveReturnedWith(3);
1404
1411
  });
1405
1412
  });
1413
+ describe('`useUseCase` should work the same as `usePseudoCoreCollection`', () => {
1414
+ test('the return value should be `PseudoCoreCollection`', () => {
1415
+ const { result } = renderHook(() => {
1416
+ return useUseCase(mathUseCase, UseCaseModes.Global);
1417
+ });
1418
+ const { current } = result;
1419
+ expect(current).toEqual([expect.any(Object), expect.any(Function)]);
1420
+ });
1421
+ test.each([
1422
+ {
1423
+ mode: UseCaseModes.Normal,
1424
+ toThrow: '',
1425
+ },
1426
+ {
1427
+ mode: UseCaseModes.Global,
1428
+ toThrow: '',
1429
+ },
1430
+ {
1431
+ mode: UseCaseModes.Stateless,
1432
+ toThrow: UseCaseModeErrors.UnsupportedStateless,
1433
+ },
1434
+ ])('does not support stateless mode', ({ mode, toThrow }) => {
1435
+ expect(() => {
1436
+ renderHook(() => {
1437
+ useUseCase(mathUseCase, mode);
1438
+ });
1439
+ // 如果上面测试成功,则手动 throw '',保证与 toThrow 的值一致
1440
+ throw '';
1441
+ }).toThrow(toThrow);
1442
+ });
1443
+ test('the return value should be `reducers` object at child component', () => {
1444
+ const onMountA = jest.fn();
1445
+ const A = () => {
1446
+ const reducers = useUseCase(mathUseCase);
1447
+ useMount(() => {
1448
+ onMountA(reducers);
1449
+ });
1450
+ return null;
1451
+ };
1452
+ const B = () => {
1453
+ const [, Provider] = useUseCase(mathUseCase, UseCaseModes.Global);
1454
+ return (_jsx(Provider, { children: _jsx(A, {}) }));
1455
+ };
1456
+ render(_jsx(B, {}));
1457
+ expect(onMountA).toHaveBeenCalledWith(expect.any(Object));
1458
+ });
1459
+ });
1406
1460
  describe('`useUseCase` should work the same as `useReducers`', () => {
1407
1461
  test('`entity` should equal `defaultFile`', () => {
1408
1462
  const { result } = renderHook(() => {
@@ -1,8 +1,21 @@
1
- import { ContextualEntityReducers } from '../../configs/defaultUseCaseContext/types';
2
- import { EntityReducers, EntityUseCase, Reducers, UseCase } from '@mic-rexjs/usecases';
1
+ import { EntityReducerMap, EntityReducers, EntityUseCase, ReducerMap, Reducers, UseCase } from '@mic-rexjs/usecases';
3
2
  import { UseCaseProvider } from '../useProvider/types';
4
3
  import { UseCaseModes } from '../../enums/UseCaseModes';
5
- export type CoreCollection<T, TEntityReducers extends EntityReducers<T>, TProvider extends UseCaseProvider | null = UseCaseProvider> = [entity: T, reducers: ContextualEntityReducers<T, TEntityReducers>, Provider: TProvider];
4
+ import { ContextualEntityReducers } from '../../configs/defaultUseCaseContext/types';
5
+ export type RootCoreCollection<T, TEntityReducers extends EntityReducerMap<T>> = [
6
+ entity: T,
7
+ reducers: ContextualEntityReducers<T, TEntityReducers>,
8
+ Provider: UseCaseProvider
9
+ ];
10
+ export type ContextualCoreCollection<T, TEntityReducers extends EntityReducerMap<T>> = [
11
+ entity: T,
12
+ reducers: ContextualEntityReducers<T, TEntityReducers>
13
+ ];
14
+ export type CoreCollection<T, TEntityReducers extends EntityReducerMap<T>> = RootCoreCollection<T, TEntityReducers> | ContextualCoreCollection<T, TEntityReducers>;
15
+ export type PseudoCoreCollection<T extends ReducerMap, TProvider extends UseCaseProvider | null = UseCaseProvider> = [
16
+ reducers: T,
17
+ Provider: TProvider
18
+ ];
6
19
  export interface EntityGetter<T> {
7
20
  (): T;
8
21
  }
@@ -32,67 +45,49 @@ export interface UseCaseHookOwnOptions<T, TUseCaseOptions extends object> extend
32
45
  options?: TUseCaseOptions;
33
46
  }
34
47
  export type UseCaseHookOptions<T, TUseCaseOptions extends object = object> = TUseCaseOptions & UseCaseHookOwnOptions<T, TUseCaseOptions>;
35
- export type ContextualCoreCollectionHookParameters<T, TEntityReducers extends EntityReducers<T>> = [
36
- usecase: EntityUseCase<T, TEntityReducers> &
48
+ export interface ContextualCoreCollectionHook {
49
+ <T, TEntityReducers extends EntityReducers<T>>(usecase: EntityUseCase<T, TEntityReducers> &
37
50
  /**
38
51
  * 如果不使用 `&`,那么很多情况下 `options` 类型无法被正确推导;
39
52
  * 因为使用 `&` 是为了让该参数 `usecase` 的泛型 `T` 推导占优先级,
40
53
  * 从而保证 `options` 里的泛型 `T` 是根据 `usecase` 来推导的。
41
54
  */
42
- UseCase<EntityReducers<T>>,
43
- options?: UseCaseHookContextualOptions<T>
44
- ];
45
- export interface ContextualCoreCollectionHook {
46
- <T, TEntityReducers extends EntityReducers<T>>(...args: ContextualCoreCollectionHookParameters<T, TEntityReducers>): CoreCollection<T, TEntityReducers, null>;
55
+ UseCase<EntityReducerMap<T>>, options?: UseCaseHookContextualOptions<T>): ContextualCoreCollection<T, TEntityReducers>;
47
56
  }
48
- export type RootCoreCollectionHookParameters<T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object> = [
49
- entity: T | EntityGetter<T>,
50
- usecase: EntityUseCase<T, TEntityReducers, TUseCaseOptions> &
57
+ export interface RootCoreCollectionHook {
58
+ <T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object>(entity: T | EntityGetter<T>, usecase: EntityUseCase<T, TEntityReducers, TUseCaseOptions> &
51
59
  /**
52
60
  * 如果不使用 `&`,那么很多情况下 `options` 类型无法被正确推导;
53
61
  * 因为使用 `&` 是为了让该参数 `usecase` 的泛型 `T` 推导占优先级,
54
62
  * 从而保证 `options` 里的泛型 `T` 是根据 `usecase` 来推导的。
55
63
  */
56
- UseCase<EntityReducers<T>, TUseCaseOptions>,
57
- options?: UseCaseHookOptions<T, TUseCaseOptions>,
58
- deps?: unknown[]
59
- ];
60
- export interface RootCoreCollectionHook {
61
- <T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object>(...args: RootCoreCollectionHookParameters<T, TEntityReducers, TUseCaseOptions>): CoreCollection<T, TEntityReducers>;
64
+ UseCase<EntityReducerMap<T>, TUseCaseOptions>, options?: UseCaseHookOptions<T, TUseCaseOptions>, deps?: unknown[]): RootCoreCollection<T, TEntityReducers>;
62
65
  }
63
- export type ModeCoreCollectionHookParameters<T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object> = [
64
- entity: T | EntityGetter<T>,
65
- usecase: EntityUseCase<T, TEntityReducers, TUseCaseOptions> &
66
+ export interface ModeCoreCollectionHook {
67
+ <T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object>(entity: T | EntityGetter<T>, usecase: EntityUseCase<T, TEntityReducers, TUseCaseOptions> &
66
68
  /**
67
69
  * 如果不使用 `&`,那么很多情况下 `options` 类型无法被正确推导;
68
70
  * 因为使用 `&` 是为了让该参数 `usecase` 的泛型 `T` 推导占优先级,
69
71
  * 从而保证 `options` 里的泛型 `T` 是根据 `usecase` 来推导的。
70
72
  */
71
- UseCase<EntityReducers<T>, TUseCaseOptions>,
72
- mode?: UseCaseModes,
73
- options?: UseCaseHookOptions<T, TUseCaseOptions>,
74
- deps?: unknown[]
75
- ];
76
- export interface ModeCoreCollectionHook {
77
- <T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object = object>(...args: ModeCoreCollectionHookParameters<T, TEntityReducers, TUseCaseOptions>): CoreCollection<T, TEntityReducers>;
73
+ UseCase<EntityReducerMap<T>, TUseCaseOptions>,
74
+ /**
75
+ * currently, only support `UseCaseModes.Stateless`
76
+ */
77
+ mode: UseCaseModes, options?: UseCaseHookOptions<T, TUseCaseOptions>, deps?: unknown[]): RootCoreCollection<T, TEntityReducers>;
78
78
  }
79
- export interface CoreCollectionHook extends ContextualCoreCollectionHook, RootCoreCollectionHook, ModeCoreCollectionHook {
79
+ export interface PseudoCoreCollectionHook {
80
+ <T extends Reducers, TUseCaseOptions extends object = object>(usecase: UseCase<T, TUseCaseOptions>,
81
+ /**
82
+ * currently, only support `UseCaseModes.Global`
83
+ */
84
+ mode: UseCaseModes, options?: TUseCaseOptions, deps?: unknown[]): PseudoCoreCollection<T>;
80
85
  }
81
- export type ReducersHookParameters<T extends Reducers, TUseCaseOptions extends object = object> = [
82
- usecase: UseCase<T, TUseCaseOptions>,
83
- options?: TUseCaseOptions,
84
- deps?: unknown[]
85
- ];
86
86
  export interface ReducersHook {
87
- <T extends Reducers, TUseCaseOptions extends object = object>(...args: ReducersHookParameters<T, TUseCaseOptions>): T;
87
+ <T extends Reducers, TUseCaseOptions extends object = object>(usecase: UseCase<T, TUseCaseOptions>, options?: TUseCaseOptions, deps?: unknown[]): T;
88
88
  }
89
- export type UseCaseHookParameters<T, TReducers extends Reducers, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object> = ContextualCoreCollectionHookParameters<T, TEntityReducers> | RootCoreCollectionHookParameters<T, TEntityReducers, TUseCaseOptions> | ModeCoreCollectionHookParameters<T, TEntityReducers, TUseCaseOptions> | ReducersHookParameters<TReducers, TUseCaseOptions>;
90
- export type UseCaseHookMaxParameters<T, TReducers extends Reducers, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object> = [
91
- entity: T | EntityGetter<T> | null,
92
- usecase: UseCase<TReducers, TUseCaseOptions> | EntityUseCase<T, TEntityReducers, TUseCaseOptions>,
93
- mode: UseCaseModes,
94
- options: TUseCaseOptions | UseCaseHookOptions<T, TUseCaseOptions> | UseCaseHookContextualOptions<T>,
95
- deps: unknown[]
96
- ];
97
- export interface UseCaseHook extends CoreCollectionHook, ReducersHook {
89
+ export interface CoreCollectionHook extends ContextualCoreCollectionHook, RootCoreCollectionHook, ModeCoreCollectionHook {
90
+ }
91
+ export type UseCaseHookParameters = Parameters<ContextualCoreCollectionHook> | Parameters<RootCoreCollectionHook> | Parameters<ModeCoreCollectionHook> | Parameters<PseudoCoreCollectionHook> | Parameters<ReducersHook>;
92
+ export interface UseCaseHook extends CoreCollectionHook, PseudoCoreCollectionHook, ReducersHook {
98
93
  }
@@ -1,3 +1,5 @@
1
- import { EntityReducers, EntityUseCase, UseCase } from '@mic-rexjs/usecases';
2
- import { UseCaseContext } from '../../configs/defaultUseCaseContext/types';
3
- export declare const useUseCaseContext: <T, TEntityReducers extends EntityReducers<T>, TUseCaseOptions extends object, TContext extends UseCaseContext<T, TEntityReducers> = UseCaseContext<T, TEntityReducers>>(usecase: EntityUseCase<T, TEntityReducers, TUseCaseOptions> & UseCase<EntityReducers<T>, TUseCaseOptions>, defaultContext?: (TContext & UseCaseContext<T, TEntityReducers>) | null | undefined) => TContext;
1
+ import { ReducerMap, UseCase } from '@mic-rexjs/usecases';
2
+ import { UseCaseContext, UseCaseContextValue } from '../../configs/defaultUseCaseContext/types';
3
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
4
+ import { UseCaseModes } from '../../enums/UseCaseModes';
5
+ export declare const useUseCaseContext: <T extends ReducerMap<import("@mic-rexjs/usecases").Reducer<unknown>>, TUseCaseOptions extends object, TContext extends UseCaseContext<UseCaseContextValue<T>>>(usecase: UseCase<T, TUseCaseOptions>, argumentTypes?: UseCaseArgumentTypes, mode?: UseCaseModes) => TContext;
@@ -2,7 +2,10 @@ import { useConstant } from '../useConstant';
2
2
  import { usecaseContextReferenceMap } from '../../configs/useCaseContextReferenceMap';
3
3
  import { createContext } from 'react';
4
4
  import { useUnmount } from 'ahooks';
5
- export const useUseCaseContext = (usecase, defaultContext) => {
5
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
6
+ import { UseCaseModes } from '../../enums/UseCaseModes';
7
+ import { defaultUseCaseContext } from '../../configs/defaultUseCaseContext';
8
+ export const useUseCaseContext = (usecase, argumentTypes = UseCaseArgumentTypes.None, mode = UseCaseModes.Normal) => {
6
9
  const map = usecaseContextReferenceMap;
7
10
  const context = useConstant(() => {
8
11
  if (map.has(usecase)) {
@@ -14,8 +17,12 @@ export const useUseCaseContext = (usecase, defaultContext) => {
14
17
  });
15
18
  return value;
16
19
  }
17
- if (defaultContext) {
18
- return defaultContext;
20
+ switch (true) {
21
+ case (mode & UseCaseModes.Global) === UseCaseModes.Global:
22
+ case (argumentTypes & UseCaseArgumentTypes.Entity) === UseCaseArgumentTypes.Entity:
23
+ break;
24
+ default:
25
+ return defaultUseCaseContext;
19
26
  }
20
27
  const ctx = createContext(null);
21
28
  map.set(usecase, {
@@ -1,41 +1,68 @@
1
1
  import { describe, expect, test } from '@jest/globals';
2
2
  import { renderHook } from '@testing-library/react';
3
3
  import { useUseCaseContext } from '.';
4
- import { objectUseCase } from '@mic-rexjs/usecases';
5
4
  import { defaultUseCaseContext } from '../../configs/defaultUseCaseContext';
6
5
  import { usecaseContextReferenceMap } from '../../configs/useCaseContextReferenceMap';
6
+ import { UseCaseModes } from '../../enums/UseCaseModes';
7
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
7
8
  const testUseCase = () => {
8
- return objectUseCase();
9
+ return {};
9
10
  };
10
11
  describe('useUseCaseContext', () => {
11
- test('should return the default context if map has not save a usecase and provide a default context', () => {
12
- const defaultContext = defaultUseCaseContext;
12
+ test('should return the default context if map has not saved a usecase with default args', () => {
13
13
  const { result } = renderHook(() => {
14
- return useUseCaseContext(testUseCase, defaultContext);
14
+ return useUseCaseContext(testUseCase);
15
15
  });
16
16
  const { current } = result;
17
17
  expect(current).toBe(defaultUseCaseContext);
18
18
  });
19
- test('should return a new context if map has not save a usecase and does not provide a default context', () => {
19
+ test('should return the default context if map has not saved a usecase with `UseCaseArgumentTypes.None` & `UseCaseModes.Normal`', () => {
20
20
  const { result } = renderHook(() => {
21
- return useUseCaseContext(testUseCase, null);
21
+ return useUseCaseContext(testUseCase, UseCaseArgumentTypes.None, UseCaseModes.Normal);
22
+ });
23
+ const { current } = result;
24
+ expect(current).toBe(defaultUseCaseContext);
25
+ });
26
+ test('should return a new context if map has not saved a usecase with `UseCaseArgumentTypes.Entity` & `UseCaseModes.Normal`', () => {
27
+ const { result } = renderHook(() => {
28
+ return useUseCaseContext(testUseCase, UseCaseArgumentTypes.Entity, UseCaseModes.Normal);
29
+ });
30
+ const { current } = result;
31
+ expect(typeof current).toBe('object');
32
+ });
33
+ test('should return a new context if map has not saved a usecase with `UseCaseArgumentTypes.None` & `UseCaseModes.Global`', () => {
34
+ const { result } = renderHook(() => {
35
+ return useUseCaseContext(testUseCase, UseCaseArgumentTypes.None, UseCaseModes.Global);
22
36
  });
23
37
  const { current } = result;
24
38
  expect(typeof current).toBe('object');
25
39
  });
26
- test.each([void 0, null, defaultUseCaseContext])('should return the existing context if map has save a usecase already', (defaultContext) => {
40
+ test.each([
41
+ {
42
+ type: UseCaseArgumentTypes.Entity,
43
+ mode: UseCaseModes.Normal,
44
+ },
45
+ {
46
+ type: UseCaseArgumentTypes.None,
47
+ mode: UseCaseModes.Global,
48
+ },
49
+ {
50
+ type: UseCaseArgumentTypes.Entity,
51
+ mode: UseCaseModes.Global,
52
+ },
53
+ ])('should return the existing context if map has save a usecase already', ({ type, mode }) => {
27
54
  const { result } = renderHook(() => {
28
- return useUseCaseContext(testUseCase) === useUseCaseContext(testUseCase, defaultContext);
55
+ return useUseCaseContext(testUseCase, type, mode) === useUseCaseContext(testUseCase);
29
56
  });
30
57
  const { current: isSame } = result;
31
58
  expect(isSame).toBe(true);
32
59
  });
33
60
  test('check context reference times when mount/unmount a usecase', () => {
34
61
  const { unmount: unmount1 } = renderHook(() => {
35
- useUseCaseContext(testUseCase);
62
+ useUseCaseContext(testUseCase, UseCaseArgumentTypes.Entity);
36
63
  });
37
64
  const { times: times1 } = usecaseContextReferenceMap.get(testUseCase);
38
- const { unmount: unmount2, rerender: rerender2 } = renderHook(() => {
65
+ const { unmount: unmount2 } = renderHook(() => {
39
66
  useUseCaseContext(testUseCase);
40
67
  useUseCaseContext(testUseCase);
41
68
  });
@@ -51,7 +78,7 @@ describe('useUseCaseContext', () => {
51
78
  });
52
79
  test('context reference times should not change when rerender hooks', () => {
53
80
  const { rerender } = renderHook(() => {
54
- useUseCaseContext(testUseCase);
81
+ useUseCaseContext(testUseCase, UseCaseArgumentTypes.Entity);
55
82
  });
56
83
  rerender();
57
84
  const { times } = usecaseContextReferenceMap.get(testUseCase);
@@ -0,0 +1,6 @@
1
+ import { UseCaseContextValue } from '../../configs/defaultUseCaseContext/types';
2
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
3
+ import { UseCaseModes } from '../../enums/UseCaseModes';
4
+ import { UseCaseStatuses } from '../../enums/UseCaseStatuses';
5
+ import { ReducerMap } from '@mic-rexjs/usecases';
6
+ export declare const useUseCaseStatuses: <T, TReducers extends ReducerMap<import("@mic-rexjs/usecases").Reducer<unknown>>>(argumentTypes: UseCaseArgumentTypes, mode: UseCaseModes, contextValue: UseCaseContextValue<TReducers> | null) => UseCaseStatuses;
@@ -0,0 +1,29 @@
1
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
2
+ import { UseCaseModes } from '../../enums/UseCaseModes';
3
+ import { UseCaseStatuses } from '../../enums/UseCaseStatuses';
4
+ import { useCreation } from 'ahooks';
5
+ export const useUseCaseStatuses = (argumentTypes, mode, contextValue) => {
6
+ return useCreation(() => {
7
+ let statuses = UseCaseStatuses.None;
8
+ const { store } = (contextValue || {});
9
+ if ((argumentTypes & UseCaseArgumentTypes.Entity) === UseCaseArgumentTypes.Entity) {
10
+ statuses |= UseCaseStatuses.EntityRootEnabled;
11
+ }
12
+ if (contextValue === null) {
13
+ statuses |= UseCaseStatuses.RootEnabled;
14
+ }
15
+ else {
16
+ statuses |= UseCaseStatuses.ContextEnabled;
17
+ }
18
+ if (store) {
19
+ statuses |= UseCaseStatuses.EntityEnabled;
20
+ }
21
+ if ((mode & UseCaseModes.Stateless) === UseCaseModes.Stateless) {
22
+ statuses |= UseCaseStatuses.StatelessEnabled;
23
+ }
24
+ if ((mode & UseCaseModes.Global) === UseCaseModes.Global) {
25
+ statuses |= UseCaseStatuses.GlobalEnabled;
26
+ }
27
+ return statuses;
28
+ }, [argumentTypes, contextValue, mode]);
29
+ };
package/es/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { UseCaseFragment } from './components/UseCaseFragment';
2
2
  export { useUseCase } from './hooks/useUseCase';
3
3
  export { useGlobalUseCase } from './hooks/useGlobalUseCase';
4
4
  export { useStatelessUseCase } from './hooks/useStatelessUseCase';
5
- export type { CoreCollection } from './hooks/useUseCase/types';
5
+ export type { RootCoreCollection, ContextualCoreCollection, CoreCollection, PseudoCoreCollection, } from './hooks/useUseCase/types';
6
6
  export type { UseCaseHookOptions } from './hooks/useUseCase/types';
7
7
  export type { UseCaseProvider } from './hooks/useProvider/types';
8
8
  export type { UseCaseFragmentProps } from './components/UseCaseFragment/types';
@@ -0,0 +1,2 @@
1
+ import { CacheableFactory, CacheCallOptions } from './types';
2
+ export declare const cacheCall: <T extends CacheableFactory>(factory: T, options?: CacheCallOptions) => T;
@@ -0,0 +1,26 @@
1
+ import { isSameArray } from '../isSameArray';
2
+ export const cacheCall = (factory, options) => {
3
+ let cache;
4
+ const { onShouldCache = () => {
5
+ return true;
6
+ }, onCompare = (newArgs, oldArgs) => {
7
+ return isSameArray(newArgs, oldArgs);
8
+ }, } = options || {};
9
+ return ((...args) => {
10
+ const cacheable = onShouldCache();
11
+ if (cache && cacheable) {
12
+ const { parameters, returnValue } = cache;
13
+ if (onCompare(parameters, args)) {
14
+ return returnValue;
15
+ }
16
+ }
17
+ const returnValue = factory(...args);
18
+ if (cacheable) {
19
+ cache = {
20
+ parameters: args,
21
+ returnValue,
22
+ };
23
+ }
24
+ return returnValue;
25
+ });
26
+ };
@@ -0,0 +1,13 @@
1
+ import { Reducer } from '@mic-rexjs/usecases';
2
+ import { RestArguments } from '@mic-rexjs/usecases/es/types';
3
+ export interface CacheableFactory {
4
+ (...args: RestArguments): unknown;
5
+ }
6
+ export interface CacheCallOptions {
7
+ onShouldCache?(): boolean;
8
+ onCompare?(newArgs: RestArguments, oldArgs: RestArguments): boolean;
9
+ }
10
+ export interface CallCache<T extends Reducer> {
11
+ parameters: Parameters<T>;
12
+ returnValue: ReturnType<T>;
13
+ }
@@ -0,0 +1,2 @@
1
+ import { CacheCallOptions, CacheableFactory } from '../cacheCall/types';
2
+ export declare const cacheCalls: <T extends Record<string, CacheableFactory>>(sourceFactories: T, options?: CacheCallOptions) => T;
@@ -0,0 +1,8 @@
1
+ import { cacheCall } from '../cacheCall';
2
+ export const cacheCalls = (sourceFactories, options) => {
3
+ const cachedFactories = {};
4
+ Object.entries(sourceFactories).forEach(([key, factory]) => {
5
+ cachedFactories[key] = cacheCall(factory, options);
6
+ });
7
+ return cachedFactories;
8
+ };
@@ -0,0 +1,5 @@
1
+ import { UseCaseHookParameters } from '../../hooks/useUseCase/types';
2
+ import { ReducerMap } from '@mic-rexjs/usecases';
3
+ import { UseCaseHookFullParameters } from './types';
4
+ import { UseCaseArgumentTypes } from '../../enums/UseCaseArgumentTypes';
5
+ export declare const fillArguments: <T, TReducers extends ReducerMap<import("@mic-rexjs/usecases").Reducer<unknown>>, TUseCaseOptions extends object>(args: UseCaseHookParameters, argumentTypes: UseCaseArgumentTypes) => UseCaseHookFullParameters<T, TReducers, TUseCaseOptions>;