@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.
- package/dist/index.min.js +1 -1
- package/es/components/UseCaseFragment/index.d.ts +2 -2
- package/es/components/UseCaseFragment/types.d.ts +3 -3
- package/es/configs/defaultUseCaseContext/index.d.ts +2 -3
- package/es/configs/defaultUseCaseContext/types.d.ts +10 -12
- package/es/configs/useCaseContextReferenceMap/types.d.ts +5 -5
- package/es/enums/UseCaseArgumentTypes/index.d.ts +5 -0
- package/es/enums/UseCaseArgumentTypes/index.js +6 -0
- package/es/enums/UseCaseModeErrors/index.d.ts +4 -0
- package/es/enums/UseCaseModeErrors/index.js +5 -0
- package/es/enums/UseCaseStatuses/index.d.ts +10 -0
- package/es/enums/UseCaseStatuses/index.js +18 -0
- package/es/hooks/useArgumentTypes/index.d.ts +3 -0
- package/es/hooks/useArgumentTypes/index.js +19 -0
- package/es/hooks/useContextualItem/index.d.ts +3 -0
- package/es/hooks/useContextualItem/index.js +13 -0
- package/es/hooks/useContextualItem/types.d.ts +3 -0
- package/es/hooks/useEntity/index.d.ts +4 -0
- package/es/hooks/useEntity/index.js +49 -0
- package/es/hooks/useGlobalUseCase/index.js +2 -8
- package/es/hooks/useGlobalUseCase/index.test.js +6 -56
- package/es/hooks/useGlobalUseCase/types.d.ts +4 -11
- package/es/hooks/useMemoizedCall/index.d.ts +2 -0
- package/es/hooks/useMemoizedCall/index.js +15 -0
- package/es/hooks/useProvider/index.d.ts +4 -4
- package/es/hooks/useProvider/index.js +16 -17
- package/es/hooks/useSafeMode/index.d.ts +3 -0
- package/es/hooks/useSafeMode/index.js +18 -0
- package/es/hooks/useStatelessUseCase/index.js +2 -5
- package/es/hooks/useStatelessUseCase/index.test.js +4 -42
- package/es/hooks/useStatelessUseCase/types.d.ts +2 -4
- package/es/hooks/useUseCase/index.js +45 -75
- package/es/hooks/useUseCase/index.test.js +74 -20
- package/es/hooks/useUseCase/types.d.ts +40 -45
- package/es/hooks/useUseCaseContext/index.d.ts +5 -3
- package/es/hooks/useUseCaseContext/index.js +10 -3
- package/es/hooks/useUseCaseContext/index.test.js +39 -12
- package/es/hooks/useUseCaseStatuses/index.d.ts +6 -0
- package/es/hooks/useUseCaseStatuses/index.js +29 -0
- package/es/index.d.ts +1 -1
- package/es/methods/cacheCall/index.d.ts +2 -0
- package/es/methods/cacheCall/index.js +26 -0
- package/es/methods/cacheCall/types.d.ts +13 -0
- package/es/methods/cacheCalls/index.d.ts +2 -0
- package/es/methods/cacheCalls/index.js +8 -0
- package/es/methods/fillArguments/index.d.ts +5 -0
- package/es/methods/fillArguments/index.js +41 -0
- package/es/methods/fillArguments/types.d.ts +10 -0
- package/es/methods/fillArguments/types.js +1 -0
- package/es/methods/getRenderingEntity/index.d.ts +3 -0
- package/es/methods/getRenderingEntity/index.js +13 -0
- package/es/methods/initEntityReducers/index.d.ts +2 -3
- package/es/methods/initEntityReducers/index.js +2 -3
- package/es/methods/triggerCallbacks/index.d.ts +1 -1
- package/es/methods/triggerCallbacks/index.js +3 -3
- package/lib/enums/UseCaseArgumentTypes/index.js +13 -0
- package/lib/enums/UseCaseModeErrors/index.js +12 -0
- package/lib/enums/UseCaseStatuses/index.js +25 -0
- package/lib/hooks/useArgumentTypes/index.js +31 -0
- package/lib/hooks/useContextualItem/index.js +21 -0
- package/lib/hooks/useEntity/index.js +63 -0
- package/lib/hooks/useGlobalUseCase/index.js +2 -15
- package/lib/hooks/useGlobalUseCase/index.test.js +5 -57
- package/lib/hooks/useMemoizedCall/index.js +22 -0
- package/lib/hooks/useProvider/index.js +18 -17
- package/lib/hooks/useSafeMode/index.js +25 -0
- package/lib/hooks/useStatelessUseCase/index.js +2 -12
- package/lib/hooks/useStatelessUseCase/index.test.js +2 -43
- package/lib/hooks/useUseCase/index.js +55 -83
- package/lib/hooks/useUseCase/index.test.js +104 -57
- package/lib/hooks/useUseCaseContext/index.js +12 -3
- package/lib/hooks/useUseCaseContext/index.test.js +45 -19
- package/lib/hooks/useUseCaseStatuses/index.js +36 -0
- package/lib/methods/cacheCall/index.js +42 -0
- package/lib/methods/cacheCalls/index.js +20 -0
- package/lib/methods/fillArguments/index.js +31 -0
- package/lib/methods/fillArguments/types.js +5 -0
- package/lib/methods/getRenderingEntity/index.js +20 -0
- package/lib/methods/initEntityReducers/index.js +2 -3
- package/lib/methods/triggerCallbacks/index.js +3 -3
- package/package.json +3 -3
- package/es/hooks/useChangeCallbackCollection/index.d.ts +0 -3
- package/es/hooks/useChangeCallbackCollection/index.js +0 -29
- package/es/methods/cacheReducerCalls/index.d.ts +0 -3
- package/es/methods/cacheReducerCalls/index.js +0 -26
- package/es/methods/cacheReducerCalls/types.d.ts +0 -11
- package/es/methods/getRootEntity/index.d.ts +0 -3
- package/es/methods/getRootEntity/index.js +0 -10
- package/es/methods/initEntityReducers/types.d.ts +0 -3
- package/es/methods/removeItem/index.d.ts +0 -1
- package/es/methods/removeItem/index.js +0 -7
- package/es/methods/toMaxArguments/index.d.ts +0 -3
- package/es/methods/toMaxArguments/index.js +0 -28
- package/lib/hooks/useChangeCallbackCollection/index.js +0 -37
- package/lib/methods/cacheReducerCalls/index.js +0 -45
- package/lib/methods/getRootEntity/index.js +0 -17
- package/lib/methods/removeItem/index.js +0 -14
- package/lib/methods/toMaxArguments/index.js +0 -25
- /package/es/{methods/cacheReducerCalls → hooks/useContextualItem}/types.js +0 -0
- /package/es/methods/{initEntityReducers → cacheCall}/types.js +0 -0
- /package/lib/{methods/cacheReducerCalls → hooks/useContextualItem}/types.js +0 -0
- /package/lib/methods/{initEntityReducers → cacheCall}/types.js +0 -0
|
@@ -1,88 +1,58 @@
|
|
|
1
|
-
import { useCreation
|
|
2
|
-
import { useContext
|
|
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 {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
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
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const {
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
|
87
|
-
}, [
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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<
|
|
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
|
|
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<
|
|
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
|
|
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<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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>(
|
|
87
|
+
<T extends Reducers, TUseCaseOptions extends object = object>(usecase: UseCase<T, TUseCaseOptions>, options?: TUseCaseOptions, deps?: unknown[]): T;
|
|
88
88
|
}
|
|
89
|
-
export
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 {
|
|
2
|
-
import { UseCaseContext } from '../../configs/defaultUseCaseContext/types';
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
|
9
|
+
return {};
|
|
9
10
|
};
|
|
10
11
|
describe('useUseCaseContext', () => {
|
|
11
|
-
test('should return the default context if map has not
|
|
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
|
|
14
|
+
return useUseCaseContext(testUseCase);
|
|
15
15
|
});
|
|
16
16
|
const { current } = result;
|
|
17
17
|
expect(current).toBe(defaultUseCaseContext);
|
|
18
18
|
});
|
|
19
|
-
test('should return
|
|
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,
|
|
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([
|
|
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
|
|
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
|
|
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,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,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>;
|