@mic-rexjs/usecases-react 1.3.32
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 +187 -0
- package/dist/index.min.js +2 -0
- package/dist/index.min.js.LICENSE.txt +9 -0
- package/es/components/UseCaseFragment/index.d.ts +3 -0
- package/es/components/UseCaseFragment/index.js +7 -0
- package/es/components/UseCaseFragment/types.d.ts +7 -0
- package/es/components/UseCaseFragment/types.js +1 -0
- package/es/configs/defaultUseCaseContext/index.d.ts +2 -0
- package/es/configs/defaultUseCaseContext/index.js +2 -0
- package/es/configs/usecaseContextMap/index.d.ts +3 -0
- package/es/configs/usecaseContextMap/index.js +1 -0
- package/es/configs/usecaseContextMap/types.d.ts +20 -0
- package/es/configs/usecaseContextMap/types.js +1 -0
- package/es/core/usecases/optionsGetterCellectionUseCase/index.d.ts +2 -0
- package/es/core/usecases/optionsGetterCellectionUseCase/index.js +13 -0
- package/es/core/usecases/optionsGetterCellectionUseCase/types.d.ts +8 -0
- package/es/core/usecases/optionsGetterCellectionUseCase/types.js +1 -0
- package/es/hooks/useOptionsTransfer/index.d.ts +3 -0
- package/es/hooks/useOptionsTransfer/index.js +15 -0
- package/es/hooks/useUseCase/index.d.ts +2 -0
- package/es/hooks/useUseCase/index.js +99 -0
- package/es/hooks/useUseCase/index.test.d.ts +1 -0
- package/es/hooks/useUseCase/index.test.js +670 -0
- package/es/hooks/useUseCase/methods/triggerCallbacks/index.d.ts +3 -0
- package/es/hooks/useUseCase/methods/triggerCallbacks/index.js +11 -0
- package/es/hooks/useUseCase/methods/triggerWatchers/index.d.ts +2 -0
- package/es/hooks/useUseCase/methods/triggerWatchers/index.js +10 -0
- package/es/hooks/useUseCase/types.d.ts +25 -0
- package/es/hooks/useUseCase/types.js +1 -0
- package/es/index.d.ts +2 -0
- package/es/index.js +2 -0
- package/lib/components/UseCaseFragment/index.js +20 -0
- package/lib/components/UseCaseFragment/types.js +5 -0
- package/lib/configs/defaultUseCaseContext/index.js +9 -0
- package/lib/configs/usecaseContextMap/index.js +8 -0
- package/lib/configs/usecaseContextMap/types.js +5 -0
- package/lib/core/usecases/optionsGetterCellectionUseCase/index.js +23 -0
- package/lib/core/usecases/optionsGetterCellectionUseCase/types.js +5 -0
- package/lib/hooks/useOptionsTransfer/index.js +23 -0
- package/lib/hooks/useUseCase/index.js +135 -0
- package/lib/hooks/useUseCase/index.test.js +951 -0
- package/lib/hooks/useUseCase/methods/triggerCallbacks/index.js +32 -0
- package/lib/hooks/useUseCase/methods/triggerWatchers/index.js +22 -0
- package/lib/hooks/useUseCase/types.js +5 -0
- package/lib/index.js +27 -0
- package/package.json +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
React-based solution for use usecases of Clean Architecture.
|
|
3
|
+
|
|
4
|
+
## Install
|
|
5
|
+
```bash
|
|
6
|
+
$ npm install --save @mic-rexjs/usecase-react
|
|
7
|
+
# -
|
|
8
|
+
$ yarn add --dev @mic-rexjs/usecase-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Links
|
|
12
|
+
- [Usage with Non-Entity Mode](#usage-with-non-entity-mode)
|
|
13
|
+
- [Useage with Entity Mode](#useage-with-entity-mode)
|
|
14
|
+
- [Test Demos](#test-demos)
|
|
15
|
+
|
|
16
|
+
## Usage with Non-Entity Mode
|
|
17
|
+
```tsx
|
|
18
|
+
// a.ts
|
|
19
|
+
import { Reducers } from '@mic-rexjs/usecases';
|
|
20
|
+
|
|
21
|
+
type MathReducers = Reducers<{
|
|
22
|
+
add(value1: number, value2: number): number;
|
|
23
|
+
|
|
24
|
+
subtraction(value1: number, value2: number): number;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
const mathUseCase = (): MathReducers => {
|
|
28
|
+
const add = (value1: number, value2: number): number => {
|
|
29
|
+
return value1 + value2;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const subtraction = (value1: number, value2: number): number => {
|
|
33
|
+
return value1 - value2;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { add, subtraction };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// b.tsx
|
|
40
|
+
const MyComponent = (): React.ReactElement => {
|
|
41
|
+
const { add, subtraction } = useUseCase(mathUseCase);
|
|
42
|
+
|
|
43
|
+
// you can use these reducers any where, and they will be never updated.
|
|
44
|
+
add(1, 2);
|
|
45
|
+
subtraction(5, 3);
|
|
46
|
+
|
|
47
|
+
// ...
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Useage with Entity Mode
|
|
52
|
+
```tsx
|
|
53
|
+
// a.ts
|
|
54
|
+
import {
|
|
55
|
+
objectUseCase,
|
|
56
|
+
ObjectReducers,
|
|
57
|
+
EntityGenerator,
|
|
58
|
+
EntityReducers
|
|
59
|
+
} from '@mic-rexjs/usecases';
|
|
60
|
+
|
|
61
|
+
interface File {
|
|
62
|
+
path: string;
|
|
63
|
+
content: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface FileUseCaseOptions {
|
|
67
|
+
maxContentLength?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// All reducers should provide the first argument with an entity type T, such as `file: T`.
|
|
71
|
+
type FileReducers<T extends File> = EntityReducers<
|
|
72
|
+
T,
|
|
73
|
+
{
|
|
74
|
+
writeFile(entity: T, content: string): EntityGenerator<T, string>;
|
|
75
|
+
isTxt(entity: T): boolean;
|
|
76
|
+
},
|
|
77
|
+
// optional to extends an existed reducers
|
|
78
|
+
ObjectReducers<T>
|
|
79
|
+
>;
|
|
80
|
+
|
|
81
|
+
const fileUseCase = <T extends File>({ maxContentLength = 2000 }: FileUseCaseOptions = {}): FileReducers<T> => {
|
|
82
|
+
/**
|
|
83
|
+
* if you have not extends an existed reducers,
|
|
84
|
+
* you should call `entityUseCase` at here,
|
|
85
|
+
* such as `const entityReducers = entityUseCase<T>()`.
|
|
86
|
+
*/
|
|
87
|
+
const objectReducers = objectUseCase<T>();
|
|
88
|
+
|
|
89
|
+
const writeFile = function* (entity: T, content: string): EntityGenerator<T, string> {
|
|
90
|
+
const { content: prevContent } = entity;
|
|
91
|
+
const newContent = prevContent + content;
|
|
92
|
+
|
|
93
|
+
if (newContent.length > maxContentLength) {
|
|
94
|
+
throw 'max length error';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// set new entity by yield expression
|
|
98
|
+
yield {
|
|
99
|
+
...entity,
|
|
100
|
+
content: newContent,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// return the new content
|
|
104
|
+
return newContent;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const isTxt = (entity: T): boolean => {
|
|
108
|
+
const { path } = entity;
|
|
109
|
+
|
|
110
|
+
return path.endsWith('.txt');
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return { ...objectReducers, writeFile, isTxt };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// b.tsx
|
|
117
|
+
import { useUseCase } from '@mic-rexjs/usecase-react';
|
|
118
|
+
import ReactDOM from 'react-dom';
|
|
119
|
+
|
|
120
|
+
const ParentComponent = ({ children }: React.PropsWithChildren): React.ReactElement => {
|
|
121
|
+
// Pass a default entity to initialize usecase, this usecase must be unique, just like `react context`.
|
|
122
|
+
const [entity, reducers, Provider] = useUseCase({ path: '', content: '' }, fileUseCase);
|
|
123
|
+
const { path } = entity;
|
|
124
|
+
const { writeFile } = reducers;
|
|
125
|
+
|
|
126
|
+
const onClick = (): void => {
|
|
127
|
+
// update entity
|
|
128
|
+
writeFile('hello world');
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
// There's no need `value` property
|
|
133
|
+
<Provider>
|
|
134
|
+
<div>
|
|
135
|
+
<header>file path is: {path}</header>
|
|
136
|
+
<main>{children}</main>
|
|
137
|
+
<footer>
|
|
138
|
+
<button onClick={onClick}>write file</button>
|
|
139
|
+
</footer>
|
|
140
|
+
</div>
|
|
141
|
+
</Provider>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const ChildComponent = (): React.ReactElement => {
|
|
146
|
+
/**
|
|
147
|
+
* use a usecase which has initialzed in parent component,
|
|
148
|
+
* just like `useContext()`, but the parameter is a usecase.
|
|
149
|
+
*/
|
|
150
|
+
const [entity, reducers] = useUseCase(fileUseCase);
|
|
151
|
+
const { content } = entity;
|
|
152
|
+
const { isTxt, writeFile } = reducers;
|
|
153
|
+
|
|
154
|
+
const onClick = (): void => {
|
|
155
|
+
// also, you can update entity at child component
|
|
156
|
+
writeFile('hello China');
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* reducers will be updated with entity change,
|
|
161
|
+
* so, you can add some reducers to deps.
|
|
162
|
+
*/
|
|
163
|
+
const ext = useMemo((): string => {
|
|
164
|
+
return isTxt() ? '.txt' : '.js';
|
|
165
|
+
}, [isTxt]);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div>
|
|
169
|
+
<header>ext: {ext}</header>
|
|
170
|
+
<main>{content}</main>
|
|
171
|
+
<footer>
|
|
172
|
+
<button onClick={onClick}>write file</button>
|
|
173
|
+
</footer>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
ReactDOM.render(
|
|
179
|
+
<ParentComponent>
|
|
180
|
+
<ChildComponent />
|
|
181
|
+
</ParentComponent>,
|
|
182
|
+
document.body
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Test Demos
|
|
187
|
+
- [useUseCase](https://github.com/mic-rexjs/usecase-react/blob/main/src/entityUseCase/index.test.ts)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see index.min.js.LICENSE.txt */
|
|
2
|
+
!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r(require("react"),require("ahooks"),require("@mic-rexjs/usecases"));else if("function"==typeof define&&define.amd)define(["react","ahooks","@mic-rexjs/usecases"],r);else{var t="object"==typeof exports?r(require("react"),require("ahooks"),require("@mic-rexjs/usecases")):r(e.react,e.ahooks,e["@mic-rexjs/usecases"]);for(var n in t)("object"==typeof exports?exports:e)[n]=t[n]}}(this,((e,r,t)=>(()=>{"use strict";var n={251:(e,r,t)=>{var n=t(156),o=Symbol.for("react.element"),u=(Symbol.for("react.fragment"),Object.prototype.hasOwnProperty),i=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,a={key:!0,ref:!0,__self:!0,__source:!0};r.jsx=function(e,r,t){var n,c={},f=null,l=null;for(n in void 0!==t&&(f=""+t),void 0!==r.key&&(f=""+r.key),void 0!==r.ref&&(l=r.ref),r)u.call(r,n)&&!a.hasOwnProperty(n)&&(c[n]=r[n]);if(e&&e.defaultProps)for(n in r=e.defaultProps)void 0===c[n]&&(c[n]=r[n]);return{$$typeof:o,type:e,key:f,ref:l,props:c,_owner:i.current}}},893:(e,r,t)=>{e.exports=t(251)},112:e=>{e.exports=t},749:e=>{e.exports=r},156:r=>{r.exports=e}},o={};function u(e){var r=o[e];if(void 0!==r)return r.exports;var t=o[e]={exports:{}};return n[e](t,t.exports,u),t.exports}u.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return u.d(r,{a:r}),r},u.d=(e,r)=>{for(var t in r)u.o(r,t)&&!u.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},u.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),u.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{u.r(i),u.d(i,{UseCaseFragment:()=>g,useUseCase:()=>O});var e=u(893),r=u(156),t=u.n(r);function n(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=new Array(r);t<r;t++)n[t]=e[t];return n}function o(e,r){if(e){if("string"==typeof e)return n(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?n(e,r):void 0}}function a(e){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a(e)}function c(e,r,t){return(r=function(e){var r=function(e,r){if("object"!==a(e)||null===e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!==a(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"===a(r)?r:String(r)}(r))in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function f(e,r){return function(e){if(Array.isArray(e))return e}(e)||function(e,r){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var n,o,u,i,a=[],c=!0,f=!1;try{if(u=(t=t.call(e)).next,0===r){if(Object(t)!==t)return;c=!1}else for(;!(c=(n=u.call(t)).done)&&(a.push(n.value),a.length!==r);c=!0);}catch(e){f=!0,o=e}finally{try{if(!c&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(f)throw o}}return a}}(e,r)||o(e,r)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}var l=u(749),s=(0,r.createContext)(null),y=new Map,p=function(e,r,t){for(var n=0,o=Object.entries(e);n<o.length;n++){var u=f(o[n],2),i=u[0],a=u[1];(null==r?void 0:r[i])!==(null==t?void 0:t[i])&&a(r,t)}};function b(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=new Array(r);t<r;t++)n[t]=e[t];return n}var v=u(112),m=["stateless","watch","onChange","options"];function d(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function h(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?d(Object(t),!0).forEach((function(r){c(e,r,t[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):d(Object(t)).forEach((function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}))}return e}var O=function(e,u,i,a){var c,d=f((0,r.useState)("function"==typeof u),1)[0],O=f((0,r.useState)(d?e:null),2),g=O[0],j=O[1],S=(d?i:u)||{},w=S.stateless,x=(S.watch,S.onChange,S.options),P=function(e,r){if(null==e)return{};var t,n,o=function(e,r){if(null==e)return{};var t,n,o={},u=Object.keys(e);for(n=0;n<u.length;n++)t=u[n],r.indexOf(t)>=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(n=0;n<u.length;n++)t=u[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}(S,m),_=(0,r.useRef)([]),C=(d?a:i)||[],A=f((0,r.useState)((function(){return d?u:e})),1)[0],E=f((0,r.useState)((function(){var e=y,t=A;if(e.has(t))return e.get(t);if(!d)return s;var n=(0,r.createContext)(null);return e.set(t,n),n})),1)[0],M=f((0,r.useContext)(E)||[],3),I=M[0],k=M[1],T=M[2],R=(0,r.useMemo)((function(){return d?w?"function"!=typeof e?e:e():g:I}),[d,I,w,g,e]),U=(0,l.useMemoizedFn)((function(){var e=h(h({},P),x);return d?(0,(0,v.entityReducerUseCase)().createEntityReducers)(R,A,h(h({},e),{},{onChange:function(e,r){d&&!w&&j(e),function(e,r,t){var n,o=function(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=function(e,r){if(e){if("string"==typeof e)return b(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?b(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var u,i=!0,a=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return i=e.done,e},e:function(e){a=!0,u=e},f:function(){try{i||null==t.return||t.return()}finally{if(a)throw u}}}}(e);try{for(o.s();!(n=o.n()).done;){var u=(0,n.value)(),i=u.onChange,a=u.watch;null==i||i(r,t),a&&p(a,r,t)}}catch(e){o.e(e)}finally{o.f()}}(_.current,e,r)},onGenerate:function(e,r){return r}})):k||A(e)})),q=r.useMemo.call(null,U,[R,U].concat(function(e){if(Array.isArray(e))return n(e)}(c=C)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(c)||o(c)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}())),D=(0,r.useMemo)((function(){return!d&&T?T:(e=_.current,{addOptionsGetter:function(r){e.unshift(r)},removeOptionsGetter:function(r){var t=e.indexOf(r);-1!==t&&e.splice(t,1)}});var e}),[d,T]),F=(0,r.useMemo)((function(){return[R,q,D]}),[R,q,D]),G=(0,l.useLatest)(F),L=f((0,r.useState)((function(){return function(e){var r=e.children,n=E.Provider;return t().createElement(n,{value:G.current},r)}})),1)[0],$=(0,r.useMemo)((function(){return d?[R,q,L]:[R,q]}),[R,q,d,L]),N=(0,r.useMemo)((function(){return E===s?q:$}),[E,q,$]);return function(e,r){var t=r.addOptionsGetter,n=r.removeOptionsGetter,o=(0,l.usePrevious)(n),u=(0,l.useMemoizedFn)((function(){return e}));(0,l.useCreation)((function(){null==o||o(u),t(u)}),[t,o,u]),(0,l.useUnmount)((function(){n(u)}))}(S,D),N},g=function(t){var n=t.usecase,o=t.watch,u=t.onChange;return O(n,{watch:o,onChange:u}),(0,e.jsx)(r.Fragment,{})}})(),i})()));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { useUseCase } from '../../hooks/useUseCase';
|
|
4
|
+
export const UseCaseFragment = ({ usecase, watch, onChange, }) => {
|
|
5
|
+
void useUseCase(usecase, { watch, onChange });
|
|
6
|
+
return _jsx(Fragment, {});
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { EntityReducers, EntityUseCase } from '@mic-rexjs/usecases';
|
|
2
|
+
import { EntityWatchMap } from '../../hooks/useUseCase/types';
|
|
3
|
+
export interface UseCaseFragmentProps<T, TReducers extends EntityReducers<T>> {
|
|
4
|
+
usecase: EntityUseCase<T, TReducers & EntityReducers<T>>;
|
|
5
|
+
watch?: EntityWatchMap<T>;
|
|
6
|
+
onChange?(prevEntity: T, nextEntity: T): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const usecaseContextMap = new Map();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { OptionsGetterCollectionReducers } from '../../core/usecases/optionsGetterCellectionUseCase/types';
|
|
3
|
+
import { AsyncEntityGenerator, EntityGenerator, EntityReducers, EntityUseCase } from '@mic-rexjs/usecases';
|
|
4
|
+
export type ContextualEntityReducer<T> = T extends (entity: infer TEntity, ...args: infer TArgs) => infer TReturn ? (...args: TArgs) => TReturn extends AsyncEntityGenerator<TEntity, infer TResult> ? Promise<TResult> : TReturn extends EntityGenerator<TEntity, infer TResult> ? TResult : TReturn : never;
|
|
5
|
+
export type ContextualEntityReducers<T> = {
|
|
6
|
+
[K in keyof T]: ContextualEntityReducer<T[K]>;
|
|
7
|
+
};
|
|
8
|
+
export type UseCaseContext<T, TReducers> = [entity: T, reducers: ContextualEntityReducers<TReducers>];
|
|
9
|
+
export type UseCaseContextWithCollectionReducers<T, TReducers, TOptions> = [
|
|
10
|
+
...context: UseCaseContext<T, TReducers>,
|
|
11
|
+
collectionReducers: OptionsGetterCollectionReducers<TOptions>
|
|
12
|
+
];
|
|
13
|
+
export interface UseCaseProvider extends React.FC<React.PropsWithChildren> {
|
|
14
|
+
}
|
|
15
|
+
export type UseCaseContextWithProvider<T, TReducers> = [
|
|
16
|
+
...args: UseCaseContext<T, TReducers>,
|
|
17
|
+
Provider: UseCaseProvider
|
|
18
|
+
];
|
|
19
|
+
export interface UseCaseContextMap<T, TReducers extends EntityReducers<T>, TOptions = object> extends Map<EntityUseCase<T, TReducers>, React.Context<UseCaseContextWithCollectionReducers<T, TReducers, TOptions> | null>> {
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const optionsGetterCollectionUseCase = (collection) => {
|
|
2
|
+
const addOptionsGetter = (getter) => {
|
|
3
|
+
collection.unshift(getter);
|
|
4
|
+
};
|
|
5
|
+
const removeOptionsGetter = (getter) => {
|
|
6
|
+
const index = collection.indexOf(getter);
|
|
7
|
+
if (index === -1) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
collection.splice(index, 1);
|
|
11
|
+
};
|
|
12
|
+
return { addOptionsGetter, removeOptionsGetter };
|
|
13
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Reducers } from '@mic-rexjs/usecases';
|
|
2
|
+
export interface OptionsGetter<T> {
|
|
3
|
+
(): T;
|
|
4
|
+
}
|
|
5
|
+
export type OptionsGetterCollectionReducers<T> = Reducers<{
|
|
6
|
+
addOptionsGetter(getter: OptionsGetter<T>): void;
|
|
7
|
+
removeOptionsGetter(getter: OptionsGetter<T>): void;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { UseCaseHookOptions } from '../useUseCase/types';
|
|
2
|
+
import { OptionsGetterCollectionReducers } from '../../core/usecases/optionsGetterCellectionUseCase/types';
|
|
3
|
+
export declare const useOptionsTransfer: <T, TOptions extends object>(options: UseCaseHookOptions<T, TOptions>, optionsGetterListReducers: OptionsGetterCollectionReducers<UseCaseHookOptions<T, TOptions>>) => void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCreation, useMemoizedFn, usePrevious, useUnmount } from 'ahooks';
|
|
2
|
+
export const useOptionsTransfer = (options, optionsGetterListReducers) => {
|
|
3
|
+
const { addOptionsGetter, removeOptionsGetter } = optionsGetterListReducers;
|
|
4
|
+
const prevRemoveOptionsGetter = usePrevious(removeOptionsGetter);
|
|
5
|
+
const getOptions = useMemoizedFn(() => {
|
|
6
|
+
return options;
|
|
7
|
+
});
|
|
8
|
+
useCreation(() => {
|
|
9
|
+
prevRemoveOptionsGetter?.(getOptions);
|
|
10
|
+
addOptionsGetter(getOptions);
|
|
11
|
+
}, [addOptionsGetter, prevRemoveOptionsGetter, getOptions]);
|
|
12
|
+
useUnmount(() => {
|
|
13
|
+
removeOptionsGetter(getOptions);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useLatest, useMemoizedFn } from 'ahooks';
|
|
2
|
+
import React, { createContext, useContext, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { defaultUseCaseContext } from '../../configs/defaultUseCaseContext';
|
|
4
|
+
import { usecaseContextMap } from '../../configs/usecaseContextMap';
|
|
5
|
+
import { optionsGetterCollectionUseCase } from '../../core/usecases/optionsGetterCellectionUseCase';
|
|
6
|
+
import { triggerCallbacks } from './methods/triggerCallbacks';
|
|
7
|
+
import { useOptionsTransfer } from '../useOptionsTransfer';
|
|
8
|
+
import { entityReducerUseCase } from '@mic-rexjs/usecases';
|
|
9
|
+
export const useUseCase = ((arg1, arg2, arg3, arg4) => {
|
|
10
|
+
const [hasDefaultEntity] = useState(typeof arg2 === 'function');
|
|
11
|
+
const [entityState, setEntityState] = useState(hasDefaultEntity ? arg1 : null);
|
|
12
|
+
const options = (hasDefaultEntity ? arg3 : arg2) || {};
|
|
13
|
+
const { stateless, watch, onChange, options: usecaseOptions, ...restUseCaseOptions } = options;
|
|
14
|
+
const optionsGetterListRef = useRef([]);
|
|
15
|
+
const deps = (hasDefaultEntity ? arg4 : arg3) || [];
|
|
16
|
+
const [usecase] = useState(() => {
|
|
17
|
+
return hasDefaultEntity
|
|
18
|
+
? arg2
|
|
19
|
+
: arg1;
|
|
20
|
+
});
|
|
21
|
+
const [context] = useState(() => {
|
|
22
|
+
const map = usecaseContextMap;
|
|
23
|
+
const entityUseCase = usecase;
|
|
24
|
+
if (map.has(entityUseCase)) {
|
|
25
|
+
return map.get(entityUseCase);
|
|
26
|
+
}
|
|
27
|
+
if (!hasDefaultEntity) {
|
|
28
|
+
return defaultUseCaseContext;
|
|
29
|
+
}
|
|
30
|
+
const ctx = createContext(null);
|
|
31
|
+
map.set(entityUseCase, ctx);
|
|
32
|
+
return ctx;
|
|
33
|
+
});
|
|
34
|
+
const [contextEntity, contextEntityReducers, contextCollectionReducers] = useContext(context) || [];
|
|
35
|
+
const entity = useMemo(() => {
|
|
36
|
+
if (!hasDefaultEntity) {
|
|
37
|
+
return contextEntity;
|
|
38
|
+
}
|
|
39
|
+
if (!stateless) {
|
|
40
|
+
return entityState;
|
|
41
|
+
}
|
|
42
|
+
if (typeof arg1 !== 'function') {
|
|
43
|
+
return arg1;
|
|
44
|
+
}
|
|
45
|
+
return arg1();
|
|
46
|
+
}, [hasDefaultEntity, contextEntity, stateless, entityState, arg1]);
|
|
47
|
+
const initReducers = useMemoizedFn(() => {
|
|
48
|
+
const mergedOptions = { ...restUseCaseOptions, ...usecaseOptions };
|
|
49
|
+
if (!hasDefaultEntity) {
|
|
50
|
+
return contextEntityReducers || usecase(mergedOptions);
|
|
51
|
+
}
|
|
52
|
+
const { createEntityReducers } = entityReducerUseCase();
|
|
53
|
+
return createEntityReducers(entity, usecase, {
|
|
54
|
+
...mergedOptions,
|
|
55
|
+
onChange(newEntity, prevEntity) {
|
|
56
|
+
if (hasDefaultEntity && !stateless) {
|
|
57
|
+
setEntityState(newEntity);
|
|
58
|
+
}
|
|
59
|
+
triggerCallbacks(optionsGetterListRef.current, newEntity, prevEntity);
|
|
60
|
+
},
|
|
61
|
+
onGenerate(newEntity, result) {
|
|
62
|
+
return result;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
const reducers = useMemo.call(null, initReducers, [entity, initReducers, ...deps]);
|
|
67
|
+
const collectionReducers = useMemo(() => {
|
|
68
|
+
if (!hasDefaultEntity && contextCollectionReducers) {
|
|
69
|
+
return contextCollectionReducers;
|
|
70
|
+
}
|
|
71
|
+
return optionsGetterCollectionUseCase(optionsGetterListRef.current);
|
|
72
|
+
}, [hasDefaultEntity, contextCollectionReducers]);
|
|
73
|
+
const contextValueWithCollectionReducers = useMemo(() => {
|
|
74
|
+
return [entity, reducers, collectionReducers];
|
|
75
|
+
}, [entity, reducers, collectionReducers]);
|
|
76
|
+
const contextValueWithCollectionReducersRef = useLatest(contextValueWithCollectionReducers);
|
|
77
|
+
const [Provider] = useState(() => {
|
|
78
|
+
return ({ children }) => {
|
|
79
|
+
const { Provider: ContextProvider } = context;
|
|
80
|
+
return React.createElement(ContextProvider, {
|
|
81
|
+
value: contextValueWithCollectionReducersRef.current,
|
|
82
|
+
}, children);
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
const contextValue = useMemo(() => {
|
|
86
|
+
if (hasDefaultEntity) {
|
|
87
|
+
return [entity, reducers, Provider];
|
|
88
|
+
}
|
|
89
|
+
return [entity, reducers];
|
|
90
|
+
}, [entity, reducers, hasDefaultEntity, Provider]);
|
|
91
|
+
const result = useMemo(() => {
|
|
92
|
+
if (context === defaultUseCaseContext) {
|
|
93
|
+
return reducers;
|
|
94
|
+
}
|
|
95
|
+
return contextValue;
|
|
96
|
+
}, [context, reducers, contextValue]);
|
|
97
|
+
void useOptionsTransfer(options, collectionReducers);
|
|
98
|
+
return result;
|
|
99
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|