From ffb11fa1387fa931499df171833e515eebc86c59 Mon Sep 17 00:00:00 2001 From: ArrayZoneYour Date: Thu, 17 Jun 2021 18:28:47 +0800 Subject: [PATCH] feat(action): add return value --- __test__/Model/return.spec.ts | 104 ++++++++++++++++++++++++++++++++++ __test__/index.d.ts | 14 +++++ __test__/index.ts | 69 ++++++++++++++++------ package.json | 2 +- src/helper.ts | 2 +- src/index.d.ts | 2 +- src/index.tsx | 4 +- src/middlewares.ts | 34 +++++------ 8 files changed, 194 insertions(+), 37 deletions(-) create mode 100644 __test__/Model/return.spec.ts diff --git a/__test__/Model/return.spec.ts b/__test__/Model/return.spec.ts new file mode 100644 index 0000000..78c153e --- /dev/null +++ b/__test__/Model/return.spec.ts @@ -0,0 +1,104 @@ +/// +import { renderHook, act } from '@testing-library/react-hooks' +import { RetTester } from '..' +import { Model } from '../../src' + +describe('action return value', () => { + test('return object value', async () => { + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.add(5) + expect(retVal).toEqual({ count: 5 }) + const retVal_2 = await actions.add(5) + expect(retVal).toEqual({ count: 5 }) + expect(retVal_2).toEqual({ count: 10 }) + }) + }) + + test('return promise value', async () => { + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.asyncAdd(5) + expect(retVal).toEqual({ count: 5 }) + const retVal_2 = await actions.asyncAdd(5) + expect(retVal).toEqual({ count: 5 }) + expect(retVal_2).toEqual({ count: 10 }) + }) + }) + + test('return produce function', async () => { + const asyncPrototype = Object.getPrototypeOf(async () => {}) + const isAsync = (input: unknown) => { + return Object.getPrototypeOf(input) === asyncPrototype + } + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.produceAdd(5) + expect(isAsync(retVal)).toBe(true) + const retVal_2 = await actions.produceAdd(5) + expect(isAsync(retVal)).toBe(true) + expect(isAsync(retVal_2)).toBe(true) + }) + }) + + test('return async produce function', async () => { + const asyncPrototype = Object.getPrototypeOf(async () => {}) + const isAsync = (input: unknown) => { + return Object.getPrototypeOf(input) === asyncPrototype + } + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.asyncProduceAdd(5) + expect(isAsync(retVal)).toBe(true) + const retVal_2 = await actions.asyncProduceAdd(5) + expect(isAsync(retVal)).toBe(true) + expect(isAsync(retVal_2)).toBe(true) + }) + }) + + test('return action', async () => { + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.hocAdd(5) + expect(retVal).toEqual({ count: 5 }) + const retVal_2 = await actions.hocAdd(5) + expect(retVal).toEqual({ count: 5 }) + expect(retVal_2).toEqual({ count: 10 }) + }) + }) + + test('return async action', async () => { + let actions: any + const { useStore } = Model(RetTester) + renderHook(() => { + ;[, actions] = useStore() + }) + await act(async () => { + const retVal = await actions.asyncHocAdd(5) + expect(retVal).toEqual({ count: 5 }) + const retVal_2 = await actions.asyncHocAdd(5) + expect(retVal).toEqual({ count: 5 }) + expect(retVal_2).toEqual({ count: 10 }) + }) + }) +}) diff --git a/__test__/index.d.ts b/__test__/index.d.ts index d165419..ac0a2fa 100644 --- a/__test__/index.d.ts +++ b/__test__/index.d.ts @@ -2,6 +2,11 @@ type CounterState = { count: number } +type RetState = { + count: number + extra: string +} + type SSRCounterState = { count: number clientKey: string @@ -33,6 +38,15 @@ type NextCounterActionParams = { add: number } +type RetActionParams = { + add: number + asyncAdd: number + produceAdd: number + asyncProduceAdd: number + hocAdd: number + asyncHocAdd: number +} + type ExtraActionParams = { add: number addCaller: undefined diff --git a/__test__/index.ts b/__test__/index.ts index 875e6e5..0c9112d 100644 --- a/__test__/index.ts +++ b/__test__/index.ts @@ -17,7 +17,7 @@ export const ActionsTester: ModelType = { actions.parse() }, parse: () => { - return state => { + return (state) => { state.data = state.response.data } } @@ -43,8 +43,8 @@ export const Counter: ModelType< addCaller: (_, { actions }) => { actions.add(5) }, - increment: params => { - return state => { + increment: (params) => { + return (state) => { state.count += params } } @@ -66,8 +66,8 @@ export const NextCounter: ModelType< addCaller: (_, { actions }) => { actions.add(5) }, - increment: params => { - return state => { + increment: (params) => { + return (state) => { state.count += params } } @@ -77,7 +77,7 @@ export const NextCounter: ModelType< // common used case interface CommonState { - xxx: string, + xxx: string yyy: number } @@ -87,7 +87,7 @@ interface CommonActionParams { export const State: ModelType = { state: { - xxx: "", + xxx: '', yyy: -1 }, actions: { @@ -123,8 +123,8 @@ export const Theme: ModelType = { export const AsyncCounter: ModelType = { actions: { - increment: params => { - return state => { + increment: (params) => { + return (state) => { state.count += params } } @@ -137,8 +137,8 @@ export const AsyncCounter: ModelType = { export const SSRCounter: ModelType = { actions: { - increment: params => { - return state => { + increment: (params) => { + return (state) => { state.count += params } } @@ -151,8 +151,8 @@ export const SSRCounter: ModelType = { export const AsyncNull: ModelType = { actions: { - increment: params => { - return state => { + increment: (params) => { + return (state) => { state.count += params } } @@ -175,6 +175,40 @@ const timeoutCounter: ModelType = { state: { count: 0 } } +export const RetTester: ModelType = { + state: { + count: 0, + extra: 'extra' + }, + actions: { + add: (num, { state }) => { + return { count: state.count + num } + }, + asyncAdd: async (num, { state }) => { + await timeout(300, {}) + return { count: state.count + num } + }, + produceAdd: (num) => { + return (state) => { + state.count += num + } + }, + asyncProduceAdd: async (num) => { + await timeout(300, {}) + return (state) => { + state.count += num + } + }, + hocAdd: (num, { actions }) => { + return actions.add(num) + }, + asyncHocAdd: async (num, { actions }) => { + await timeout(100, {}) + return actions.add(num) + } + } +} + export const TimeoutCounter = Model(timeoutCounter) export const ErrorCounter: ModelType = { @@ -196,8 +230,8 @@ export const NextCounterModel: ModelType< NextCounterActionParams > = { actions: { - add: num => { - return state => { + add: (num) => { + return (state) => { state.count += num } }, @@ -235,7 +269,10 @@ Array.from(Array(20000).keys()).forEach((_, idx) => { }) console.timeEnd('create data') -export const ExpensiveModel: ModelType = { +export const ExpensiveModel: ModelType< + ExpensiveState, + ExpensiveActionParams +> = { state: { moduleList: [] }, diff --git a/package.json b/package.json index aef16c8..0591573 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-model", - "version": "4.0.1", + "version": "4.0.2", "description": "The State management library for React", "main": "./dist/react-model.js", "module": "./dist/react-model.esm.js", diff --git a/src/helper.ts b/src/helper.ts index 11e8963..4c3cbde 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -35,7 +35,7 @@ const consumerAction = ( params, type: 'outer' } - await applyMiddlewares(actionMiddlewares, context) + return await applyMiddlewares(actionMiddlewares, context) } const consumerActions = ( diff --git a/src/index.d.ts b/src/index.d.ts index d176577..4a7963e 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -96,7 +96,7 @@ type Context = InnerContext & { modelMiddlewares?: Middleware[] } -type Middleware = (C: Context, M: Middleware[]) => void +type Middleware = (C: Context, M: Middleware[]) => Promise type MiddlewareConfig = { logger: { diff --git a/src/index.tsx b/src/index.tsx index 908b9af..65d206b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -211,9 +211,9 @@ const getActions = ( Global } if (Global.Middlewares[modelName]) { - await applyMiddlewares(Global.Middlewares[modelName], context) + return await applyMiddlewares(Global.Middlewares[modelName], context) } else { - await applyMiddlewares(actionMiddlewares, context) + return await applyMiddlewares(actionMiddlewares, context) } }) ) diff --git a/src/middlewares.ts b/src/middlewares.ts index eb62ce5..92a9dad 100644 --- a/src/middlewares.ts +++ b/src/middlewares.ts @@ -16,9 +16,9 @@ const config: MiddlewareConfig = { const tryCatch: Middleware = async (context, restMiddlewares) => { const { next } = context if (config.tryCatch.enable) { - await next(restMiddlewares).catch((e: any) => console.log(e)) + return await next(restMiddlewares).catch((e: any) => console.log(e)) } else { - await next(restMiddlewares) + return await next(restMiddlewares) } } @@ -31,7 +31,7 @@ const getNewState: Middleware = async (context, restMiddlewares) => { ...(Global.Context['__global'] || {}), ...(Global.Context[modelName] || {}) })) || null - await next(restMiddlewares) + return await next(restMiddlewares) } const getNewStateWithCache = (maxTime: number = 5000): Middleware => async ( @@ -55,7 +55,7 @@ const getNewStateWithCache = (maxTime: number = 5000): Middleware => async ( }), timeout(maxTime, getCache(modelName, actionName)) ])) || null - await next(restMiddlewares) + return await next(restMiddlewares) } const setNewState: Middleware = async (context, restMiddlewares) => { @@ -72,7 +72,7 @@ const setNewState: Middleware = async (context, restMiddlewares) => { } if (newState) { setPartialState(modelName, newState) - await next(restMiddlewares) + return await next(restMiddlewares) } } @@ -88,7 +88,7 @@ const stateUpdater: Middleware = async (context, restMiddlewares) => { ) { setter[__hash].setState(Global.State[modelName]) } - await next(restMiddlewares) + return await next(restMiddlewares) } const subscription: Middleware = async (context, restMiddlewares) => { @@ -99,7 +99,7 @@ const subscription: Middleware = async (context, restMiddlewares) => { callback(context) }) } - await next(restMiddlewares) + return await next(restMiddlewares) } const consoleDebugger: Middleware = async (context, restMiddlewares) => { @@ -128,27 +128,29 @@ const consoleDebugger: Middleware = async (context, restMiddlewares) => { context.actionName, `payload: ${context.params}` ) - await context.next(restMiddlewares) + const ret = await context.next(restMiddlewares) console.log( '%c Next', `color: #4CAF50; font-weight: bold`, Global.State[context.modelName] ) console.groupEnd() + return ret } else { - await context.next(restMiddlewares) + return await context.next(restMiddlewares) } } const devToolsListener: Middleware = async (context, restMiddlewares) => { const { Global } = context - await context.next(restMiddlewares) + const ret = await context.next(restMiddlewares) if (Global.withDevTools && config.devtools.enable) { Global.devTools.send( `${context.modelName}_${context.actionName}`, Global.State ) } + return ret } const communicator: Middleware = async (context, restMiddlewares) => { @@ -172,7 +174,7 @@ const communicator: Middleware = async (context, restMiddlewares) => { } }) } - await next(restMiddlewares) + return await next(restMiddlewares) } const actionMiddlewares = [ @@ -203,12 +205,12 @@ const applyMiddlewares = async ( middlewares: Middleware[], context: BaseContext ) => { - context.next = (restMiddlewares: Middleware[]) => - restMiddlewares.length > 0 && - restMiddlewares[0](context as Context, restMiddlewares.slice(1)) - if (middlewares.length > 0) { - await middlewares[0](context as Context, middlewares.slice(1)) + context.next = (restMiddlewares: Middleware[]) => { + if (restMiddlewares.length > 0) + return restMiddlewares[0](context as Context, restMiddlewares.slice(1)) + else return context.newState } + return await middlewares[0](context as Context, middlewares.slice(1)) } export { actionMiddlewares, applyMiddlewares, middlewares }