Add ability to easily create redux middleware for async actions

This commit is contained in:
Jerko Steiner 2019-08-01 15:51:59 +07:00
parent fa27dda530
commit dca9691d7d
3 changed files with 79 additions and 21 deletions

View File

@ -6,11 +6,11 @@ import bodyParser from 'body-parser'
import express from 'express'
import {AddressInfo} from 'net'
import {Server} from 'http'
import {createReduxClient} from './redux'
import {createReduxClient, createReducer} from './redux'
import {createRemoteClient} from './remote'
import {jsonrpc} from './express'
import {keys} from 'ts-transformer-keys'
import {TActionCreators, TAllActions} from './types'
import {TPendingActions, TAllActions} from './types'
describe('createReduxClient', () => {
@ -73,9 +73,33 @@ describe('createReduxClient', () => {
// type R<T> = T extends (...args: any[]) => infer RV ? RV : never
type Client = typeof client
type ActionCreators = TActionCreators<typeof client>
type ActionCreators = TPendingActions<typeof client>
type AllActions = TAllActions<typeof client>
const defaultState = {
sum: 1,
}
const create = createReducer('myService', defaultState)
<typeof client>((state, action) => {
switch (action.method) {
case 'add':
const r1: number = action.payload
return state
case 'addAsync':
const r2: number = action.payload
return state
case 'addStringsAsync':
const r3: string = action.payload
return state
case 'addWithContext':
const r4: number = action.payload
return state
default:
return state
}
})
function handleAction(state: any, action: AllActions) {
if (action.type !== 'myService') {
return
@ -99,9 +123,6 @@ describe('createReduxClient', () => {
}
}
// type Values<T> = T[keyof T]
// type C = ReturnType<Values<typeof client>>
return client
}

View File

@ -1,4 +1,10 @@
import {TAsyncified, TReduxed} from './types'
import {
IReduxed,
TAsyncified,
TReduxed,
TResolvedActions,
TAllActions,
} from './types'
import {createRemoteClient} from './remote'
export function createReduxClient<T, ActionType extends string>(
@ -20,3 +26,26 @@ export function createReduxClient<T, ActionType extends string>(
return service as TReduxed<T, ActionType>
}
export const createReducer = <ActionType extends string, State>(
actionType: ActionType,
defaultState: State,
) => <R extends IReduxed<ActionType>>(
handleAction: (state: State, action: TResolvedActions<R>) => State,
) => (state: State = defaultState, action?: TAllActions<R>): State => {
if (!action) {
return state
}
if (action.type !== actionType) {
return state
}
if (action.status === 'pending') {
// TODO handle loading
return state
}
if (action.status === 'rejected') {
// TODO handle rejected
return state
}
return handleAction(state, action)
}

View File

@ -2,6 +2,7 @@ type ArgumentTypes<T> =
T extends (...args: infer U) => infer R ? U : never
type RetType<T> = T extends (...args: any[]) => infer R ? R : never
type UnwrapHOC<T> = T extends (...args: any[]) => infer R ? R : T
type UnwrapPromise<T> = T extends Promise<infer V> ? V : T
type RetProm<T> = T extends Promise<any> ? T : Promise<T>
type PromisifyReturnType<T> = (...a: ArgumentTypes<T>) =>
RetProm<UnwrapHOC<RetType<T>>>
@ -12,19 +13,13 @@ export type TAsyncified<T> = {
[K in keyof T]: PromisifyReturnType<T[K]>
}
export interface IReduxed<ActionType extends string> {
[key: string]: (...a: any[]) => IRPCPendingAction<any, ActionType, typeof key>
}
export type TReduxed<T, ActionType extends string> = {
// [K in keyof T]: (...a: ArgumentTypes<T[K]>) => {
// type: ActionType
// payload: RetProm<UnwrapHOC<RetType<T[K]>>>
// method: K
// status: 'pending'
// }
// [K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
// IPendingAction<RetProm<UnwrapHOC<RetType<T[K]>>>, ActionType> & {
// method: K,
// }
[K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
IRPCPendingAction<UnwrapHOC<RetType<T[K]>>, ActionType, K>
IRPCPendingAction<UnwrapPromise<UnwrapHOC<RetType<T[K]>>>, ActionType, K>
}
export interface IRPCPendingAction<
@ -45,6 +40,13 @@ export interface IRPCRejectedAction<
method: Method
}
export type TRPCAction<
T, ActionType extends string, Method extends string | number | symbol
> =
IRPCPendingAction<T, ActionType, Method>
| IRPCResolvedAction<T, ActionType, Method>
| IRPCRejectedAction<ActionType, Method>
export type TResolved<A> =
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
? IRPCResolvedAction<T, ActionType, Method>
@ -55,7 +57,13 @@ export type TRejected<A> =
? IRPCRejectedAction<ActionType, Method>
: never
export type TPending<A> =
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
? IRPCPendingAction<T, ActionType, Method>
: never
type Values<T> = T[keyof T]
export type TActionCreators<T> = RetType<Values<T>>
export type TAllActions<T> = TActionCreators<T>
| TResolved<TActionCreators<T>> | TRejected<TActionCreators<T>>
export type TPendingActions<T> = TPending<RetType<Values<T>>>
export type TResolvedActions<T> = TResolved<TPendingActions<T>>
export type TAllActions<T> = TPendingActions<T>
| TResolvedActions<T> | TRejected<TPendingActions<T>>