@mic-rexjs/usecases-react
v2.6.0
Published
React-based solution for use usecases of Clean Architecture
Downloads
544
Maintainers
Readme
Description
React-based solution for use usecases of Clean Architecture.
Install
$ npm install --save @mic-rexjs/usecases-react
# -
$ yarn add --dev @mic-rexjs/usecases-react
Links
Usage with Non-Entity Mode
// a.ts
import { Reducers } from '@mic-rexjs/usecases';
type MathReducers = Reducers<{
add(value1: number, value2: number): number;
subtraction(value1: number, value2: number): number;
}>;
const mathUseCase = (): MathReducers => {
const add = (value1: number, value2: number): number => {
return value1 + value2;
};
const subtraction = (value1: number, value2: number): number => {
return value1 - value2;
};
return { add, subtraction };
};
// b.tsx
const MyComponent = (): React.ReactElement => {
const { add, subtraction } = useUseCase(mathUseCase);
// you can use these reducers any where, and they will be never updated.
add(1, 2);
subtraction(5, 3);
// ...
};
Useage with Entity Mode
// a.ts
import {
objectUseCase,
ObjectReducers,
EntityGenerator,
EntityReducers
} from '@mic-rexjs/usecases';
interface File {
path: string;
content: string;
}
interface FileUseCaseOptions {
maxContentLength?: number;
}
// All reducers should provide the first argument with an entity type T, such as `file: T`.
type FileReducers<T extends File> = EntityReducers<
T,
{
writeFile(entity: T, content: string): EntityGenerator<T, string>;
isTxt(entity: T): boolean;
},
// optional to extends an existed reducers
ObjectReducers<T>
>;
const fileUseCase = <T extends File>({ maxContentLength = 2000 }: FileUseCaseOptions = {}): FileReducers<T> => {
/**
* if you have not extends an existed reducers,
* you should call `entityUseCase` at here,
* such as `const entityReducers = entityUseCase<T>()`.
*/
const objectReducers = objectUseCase<T>();
const writeFile = function* (entity: T, content: string): EntityGenerator<T, string> {
const { content: oldContent } = entity;
const newContent = oldContent + content;
if (newContent.length > maxContentLength) {
throw 'max length error';
}
// set new entity by yield expression
yield {
...entity,
content: newContent,
};
// return the new content
return newContent;
};
const isTxt = (entity: T): boolean => {
const { path } = entity;
return path.endsWith('.txt');
};
return { ...objectReducers, writeFile, isTxt };
};
// b.tsx
import ReactDOM from 'react-dom';
import { useUseCase } from '@mic-rexjs/usecases-react';
const ParentComponent = ({ children }: React.PropsWithChildren): React.ReactElement => {
// Pass a default entity to initialize usecase, this usecase must be unique, just like `react context`.
const [entity, reducers, Provider] = useUseCase({ path: '', content: '' }, fileUseCase);
const { path } = entity;
const { writeFile } = reducers;
const onClick = (): void => {
// update entity
writeFile('hello world');
};
return (
// There's no need `value` property
<Provider>
<div>
<header>file path is: {path}</header>
<main>{children}</main>
<footer>
<button onClick={onClick}>write file</button>
</footer>
</div>
</Provider>
);
};
const ChildComponent = (): React.ReactElement => {
/**
* use a usecase which has initialzed in parent component,
* just like `useContext()`, but the parameter is a usecase.
*/
const [entity, reducers] = useUseCase(fileUseCase);
const { content } = entity;
const { isTxt, writeFile } = reducers;
const onClick = (): void => {
// also, you can update entity at child component
writeFile('hello China');
};
/**
* reducers will be updated with entity change,
* so, you can add some reducers to deps.
*/
const ext = useMemo((): string => {
return isTxt() ? '.txt' : '.js';
}, [isTxt]);
return (
<div>
<header>ext: {ext}</header>
<main>{content}</main>
<footer>
<button onClick={onClick}>write file</button>
</footer>
</div>
);
};
ReactDOM.render(
<ParentComponent>
<ChildComponent />
</ParentComponent>,
document.body
);