UNPKG

redux-loop

Version:

Sequence your effects naturally and purely by returning them from your reducers.

264 lines (218 loc) 7.29 kB
import { Action, ActionCreator, AnyAction, StoreEnhancer, Store } from 'redux'; export type UnknownAction = AnyAction | never; export interface StoreCreator { <S, A extends Action>( reducer: LoopReducer<S, A>, preloadedState: S | undefined, enhancer: StoreEnhancer<S> ): Store<S>; } type WithDefaultActionHandling<T extends AnyAction> = | T | Action<'@@REDUX_LOOP/ENFORCE_DEFAULT_HANDLING'>; export type Loop<S> = [S, CmdType]; export interface LoopReducer<S, A extends Action = AnyAction> { (state: S | undefined, action: WithDefaultActionHandling<A>, ...args: any[]): | S | Loop<S>; } export interface LoopReducerWithDefinedState<S, A extends Action = AnyAction> { (state: S, action: WithDefaultActionHandling<A>, ...args: any[]): S | Loop<S>; } export interface LiftedLoopReducer<S, A extends Action = AnyAction> { ( state: S | undefined, action: WithDefaultActionHandling<A>, ...args: any[] ): Loop<S>; } export type CmdSimulation = { result: any; success: boolean; }; export interface MultiCmdSimulation { [index: number]: CmdSimulation | MultiCmdSimulation; } export interface NoneCmd { readonly type: 'NONE'; simulate(): null; } export interface ListCmd { readonly type: 'LIST'; readonly cmds: CmdType[]; readonly sequence?: boolean; readonly batch?: boolean; simulate(simulations: MultiCmdSimulation): AnyAction[]; } export interface ActionCmd<A extends Action> { readonly type: 'ACTION'; readonly actionToDispatch: A; simulate(): A; } export interface SetTimeoutCmd<A extends Action = never> { readonly type: 'SET_TIMEOUT'; readonly nestedCmd: CmdType; readonly delayMs: number; readonly scheduledActionCreator?: (timerId: number) => A; simulate(timerId: number, nestedSimulation?: CmdSimulation | MultiCmdSimulation): A[] | A | null } export interface SetIntervalCmd<A extends Action = never> { readonly type: 'SET_INTERVAL'; readonly nestedCmd: CmdType; readonly delayMs: number; readonly scheduledActionCreator?: (timerId: number) => A; simulate(timerId: number, nestedSimulation?: CmdSimulation | MultiCmdSimulation): A[] | A | null } export interface MapCmd<A extends Action = never> { readonly type: 'MAP'; readonly tagger: ActionCreator<A>; readonly nestedCmd: CmdType; readonly args: any[]; simulate(simulations?: CmdSimulation | MultiCmdSimulation): A[] | A | null; } export interface RunCmd< SuccessAction extends Action = never, FailAction extends Action = never > { readonly type: 'RUN'; readonly func: (...args: any[]) => any; readonly args?: any[]; readonly failActionCreator?: ActionCreator<FailAction>; readonly successActionCreator?: ActionCreator<SuccessAction>; readonly forceSync?: boolean; simulate(simulation: CmdSimulation): SuccessAction | FailAction; } export type CmdType = | ActionCmd<UnknownAction> | SetTimeoutCmd<UnknownAction> | SetIntervalCmd<UnknownAction> | ListCmd | MapCmd<UnknownAction> | NoneCmd | RunCmd<UnknownAction, UnknownAction>; export interface LoopConfig { readonly DONT_LOG_ERRORS_ON_HANDLED_FAILURES?: boolean; readonly ENABLE_THUNK_MIGRATION?: boolean; } export function install<S>(config?: LoopConfig): StoreEnhancer<S>; export function loop<S>(state: S, cmd: CmdType): Loop<S>; export namespace Cmd { export const dispatch: unique symbol; export const getState: unique symbol; export const none: NoneCmd; export type Dispatch = <A extends Action>(a: A) => Promise<A>; export type GetState = <S>() => S; export function action<A extends Action>(action: A): ActionCmd<A>; export type ListOptions = { batch?: boolean; sequence?: boolean; testInvariants?: boolean; }; export function list(cmds: CmdType[], options?: ListOptions): ListCmd; export function clearTimeout(timerId: number): RunCmd; export function clearInterval(timerId: number): RunCmd; export function setTimeout<A extends Action = never>( cmd: CmdType, delayMs: number, options?: { scheduledActionCreator?: (timerId: number) => A; }, ): SetTimeoutCmd<A>; export function setInterval<A extends Action = never>( cmd: CmdType, delayMs: number, options?: { scheduledActionCreator?: (timerId: number) => A; }, ): SetIntervalCmd<A>; export function map<A extends Action, B extends Action>( cmd: CmdType, tagger: (subAction: B) => A, args?: any[] ): MapCmd<A>; // Allow the use of special dispatch | getState symbols type ArgOrSymbol<T> = { [K in keyof T]: T[K] extends GetState ? typeof getState : T[K] extends Dispatch ? typeof dispatch : T[K]; }; type RunFunc = (...args: any[]) => Promise<any> | any; export type PromiseResult<T> = T extends Promise<infer U> ? U : T; export type RunOptions< Func extends RunFunc, SuccessAction extends Action = never, FailAction extends Action = never, FailReason = unknown > = { args?: ArgOrSymbol<Parameters<Func>>; forceSync?: boolean; testInvariants?: boolean; successActionCreator: ( value: PromiseResult<ReturnType<Func>> ) => SuccessAction; failActionCreator: (error: FailReason) => FailAction; }; export function run<Func extends RunFunc>( f: Func, options?: Omit< RunOptions<Func>, 'successActionCreator' | 'failActionCreator' > ): RunCmd<never, never>; export function run<Func extends RunFunc, SuccessAction extends Action>( f: Func, options: Omit<RunOptions<Func, SuccessAction>, 'failActionCreator'> ): RunCmd<SuccessAction, never>; export function run< Func extends RunFunc, FailAction extends Action, FailReason = unknown >( f: Func, options: Omit< RunOptions<Func, never, FailAction, FailReason>, 'successActionCreator' > ): RunCmd<never, FailAction>; export function run< Func extends RunFunc, SuccessAction extends Action, FailAction extends Action, FailReason = unknown >( f: Func, options: RunOptions<Func, SuccessAction, FailAction, FailReason> ): RunCmd<SuccessAction, FailAction>; } export type ReducersMapObject<S, A extends Action = AnyAction> = { [K in keyof S]: LoopReducer<S[K], A>; }; export function combineReducers<S, A extends Action = AnyAction>( reducers: ReducersMapObject<S, A> ): LiftedLoopReducer<S, A>; export function mergeChildReducers<S>( parentResult: S | Loop<S>, action: AnyAction, childMap: ReducersMapObject<S> ): Loop<S>; // eslint-disable-next-line camelcase export function DEPRECATED_mergeChildReducers<S>( parentResult: S | Loop<S>, action: AnyAction, childMap: ReducersMapObject<S> ): Loop<S>; export function reduceReducers<S>( initialReducer: LoopReducer<S, any>, ...reducers: Array<LoopReducerWithDefinedState<S, any>> ): LiftedLoopReducer<S, any>; export function reduceReducers<S, A extends Action>( initialReducer: LoopReducer<S, A>, ...reducers: Array<LoopReducerWithDefinedState<S, A>> ): LiftedLoopReducer<S, A>; export function liftState<S>(state: S | Loop<S>): Loop<S>; export function isLoop(test: any): boolean; export function getModel<S>(loop: S | Loop<S>): S; export function getCmd(a: any): CmdType | null; export type HandlerReturn<S> = S | Loop<S>;