react-model
Version:
The State management library for React
278 lines (250 loc) • 7.01 kB
TypeScript
type Setter = {
classSetter: ClassSetter
functionSetter: FunctionSetter
}
type FunctionSetter = {
[modelName: string]: {
[actionName: string]: {
setState: React.Dispatch<any>
selector?: Function
selectorRef?: unknown
}
}
}
type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false
interface Global {
Actions: {
[modelName: string]: {
[actionName: string]: Action
}
}
State: {
[modelName: string]: any
}
mutableState: {
[modelName: string]: any
}
AsyncState: {
[modelName: string]: undefined | ((context?: any) => Promise<Partial<any>>)
}
Context: any
Middlewares: {
[modelName: string]: Middleware[]
}
subscriptions: Subscriptions
Setter: Setter
devTools: any
withDevTools: boolean
gid: number
uid: number
storeId: number
currentStoreId: string
}
type ClassSetter = React.Dispatch<any> | undefined
type Action<S = {}, P = any, ActionKeys = {}, ExtContext extends {} = {}> = (
params: P,
context: {
state: S
actions: getConsumerActionsType<Actions<S, ActionKeys, ExtContext>>
} & ExtContext
) =>
| Partial<S>
| Promise<Partial<S> | ProduceFunc<S> | void>
| ProduceFunc<S>
| void
type ProduceFunc<S> = (state: S) => void
// v3.0 Actions
type Actions<S = {}, ActionKeys = {}, ExtContext extends object = {}> = {
[P in keyof ActionKeys]: Action<S, ActionKeys[P], ActionKeys, ExtContext>
}
// v4.1+ Custom Hooks
type CustomModelHook<State> = () => State
type Dispatch<A> = (value: A) => void
type SetStateAction<S> = S | ((prevState: S) => S)
interface ModelContext {
modelName: string
}
interface BaseContext<S = {}, P = any> {
action: Action
consumerActions: (
actions: Actions,
modelContext: ModelContext
) => getConsumerActionsType<Actions>
params: P
middlewareConfig?: Object
actionName: string
modelName: string
next?: Function
disableSelectorUpdate?: boolean
newState: Global['State'] | Function | null
Global: Global
}
interface InnerContext<S = {}> extends BaseContext<S> {
// Actions with function type context will always invoke current component's reload.
// f -> function, o -> outer, c -> class, u -> useModel
type?: 'f' | 'o' | 'c' | 'u'
__hash?: string
}
type Context<S = {}> = InnerContext<S> & {
next: Function
modelMiddlewares?: Middleware[]
}
type Middleware<S = {}> = (C: Context<S>, M: Middleware<S>[]) => Promise<void>
type MiddlewareConfig = {
logger: {
enable: boolean | ((context: BaseContext) => boolean)
}
devtools: { enable: boolean }
tryCatch: { enable: boolean }
}
interface Models<State = any, ActionKeys = any, ExtContext extends {} = {}> {
[name: string]:
| ModelType<State, ActionKeys, ExtContext>
| API<ModelType<State, ActionKeys, {}>>
}
type Selector<S, R> = (state: S) => R
interface API<MT extends ModelType = ModelType<any, any, {}>> {
__id: string
__ERROR__?: boolean
useStore: <
F extends Selector<Get<MT, 'state'>, any> = Selector<
Get<MT, 'state'>,
unknown
>
>(
selector?: F
) => [
F extends Selector<Get<MT, 'state'>, any>
? Equals<F, Selector<Get<MT, 'state'>, unknown>> extends true
? Get<MT, 'state'>
: ReturnType<F>
: Get<MT, 'state'>,
getConsumerActionsType<Get<MT, 'actions'>>
]
getState: () => Readonly<Get<MT, 'state'>>
subscribe: (
actionName: keyof MT['actions'] | Array<keyof MT['actions']>,
callback: (context: BaseContext) => void
) => void
unsubscribe: (
actionName: keyof Get<MT, 'actions'> | Array<keyof Get<MT, 'actions'>>
) => void
actions: Readonly<getConsumerActionsType<Get<MT, 'actions'>>>
}
interface LaneAPI<S> {
useStore: () => S
getState: () => S
getStore: () => S | undefined
subscribe: (callback: () => void) => void
unsubscribe: (callback: () => void) => void
}
interface APIs<M extends Models> {
useStore: <
K extends keyof M,
S extends M[K] extends API
? ArgumentTypes<Get<M[K], 'useStore'>>[1]
: M[K] extends ModelType
? Selector<Get<M[K], 'state'>, unknown>
: any
>(
name: K,
selector?: S
) => M[K] extends API
? S extends (...args: any) => void
? Equals<S, undefined> extends true
? [ReturnType<Get<M[K], 'getState'>>, Get<M[K], 'actions'>]
: ReturnType<S>
: ReturnType<Get<M[K], 'useStore'>>
: M[K] extends ModelType
? S extends (...args: any) => void
? [
Equals<ReturnType<S>, unknown> extends true
? Get<M[K], 'state'>
: ReturnType<S>,
getConsumerActionsType<Get<M[K], 'actions'>>
]
: [Get<M[K], 'state'>, getConsumerActionsType<Get<M[K], 'actions'>>]
: any
getState: <K extends keyof M>(
modelName: K
) => M[K] extends ModelType
? Readonly<Get<M[K], 'state'>>
: M[K] extends API
? ReturnType<Get<M[K], 'getState'>>
: any
getActions: <K extends keyof M>(
modelName: K
) => M[K] extends ModelType
? Readonly<getConsumerActionsType<Get<M[K], 'actions'>>>
: M[K] extends API
? M[K]['actions']
: unknown
getInitialState: <T extends any>(
context?: T | undefined,
config?: { isServer: boolean }
) => Promise<{
[modelName: string]: any
}>
subscribe: <K extends keyof M>(
modelName: K,
actionName: keyof Get<M[K], 'actions'> | Array<keyof Get<M[K], 'actions'>>,
callback: (context: BaseContext) => void
) => void
unsubscribe: <K extends keyof M>(
modelName: K,
actionName: keyof Get<M[K], 'actions'> | Array<keyof Get<M[K], 'actions'>>
) => void
actions: {
[K in keyof M]: M[K] extends API
? M[K]['actions']
: Readonly<getConsumerActionsType<Get<M[K], 'actions'>>>
}
}
// v3.0
type ModelType<
InitStateType = any,
ActionKeys = any,
ExtContext extends {} = {}
> = {
__ERROR__?: boolean
actions: {
[P in keyof ActionKeys]: Action<
InitStateType,
ActionKeys[P],
ActionKeys,
ExtContext
>
}
middlewares?: Middleware[]
state: InitStateType
asyncState?: (context?: any) => Promise<Partial<InitStateType>>
}
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any
? A
: never
// v3.0
// TODO: ArgumentTypes<A[P]>[0] = undefined | string
type getConsumerActionsType<A extends Actions<any, any, any>> = {
[P in keyof A]: ArgumentTypes<A[P]>[0] extends undefined
? (params?: ArgumentTypes<A[P]>[0]) => ReturnType<A[P]>
: (params: ArgumentTypes<A[P]>[0]) => ReturnType<A[P]>
}
type Get<Object, K extends keyof Object> = Object[K]
type ModelsProps<M extends Models> = {
useStore: <K extends keyof M>(
name: K,
models?: M
) => M[K] extends ModelType
? [Get<M[K], 'state'>, getConsumerActionsType<Get<M[K], 'actions'>>]
: M[K]
getState: <K extends keyof M>(
modelName: K
) => M[K] extends ModelType ? Readonly<Get<M[K], 'state'>> : M[K]
}
type Subscriptions = {
[key: string]: Function[]
}