mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
refactor: transform connect
This commit is contained in:
parent
91bbb64a83
commit
29ac1d88b9
2 changed files with 118 additions and 71 deletions
|
|
@ -1,71 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
const middlewares = []
|
||||
|
||||
export function addMiddleware(middleware) {
|
||||
if (typeof middleware !== 'function') return null
|
||||
const m = middleware.bind(null)
|
||||
middlewares.push(m)
|
||||
return function removeMiddleware() {
|
||||
const index = middlewares.indexOf(m)
|
||||
if (index === -1) return
|
||||
middlewares.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function applyMiddlewares(method, args) {
|
||||
for(const middleware of middlewares) {
|
||||
[method = method, args = args] = middleware(method, args) || []
|
||||
}
|
||||
return [method, args]
|
||||
}
|
||||
|
||||
function run([method, args]) {
|
||||
method.apply(null, args)
|
||||
}
|
||||
|
||||
function link(instance, sources) {
|
||||
const wrappedMethods = {/* [keyof sources] -> wrappedMethods.method */}
|
||||
const map = new Map(/* sources.creator -> wrappedMethods.method */)
|
||||
|
||||
Object.entries(sources).forEach(([key, createMethod]) => {
|
||||
const method = createMethod(dispatch)
|
||||
wrappedMethods[key] = method
|
||||
map.set(createMethod, method)
|
||||
})
|
||||
|
||||
const sourcesValues = Object.values(sources)
|
||||
function dispatch(...args) {
|
||||
const isFromSource = sourcesValues.includes(args[0])
|
||||
if (isFromSource) {
|
||||
const [createMethod, ...otherArgs] = args
|
||||
const method = map.get(createMethod)
|
||||
run(applyMiddlewares(method, otherArgs))
|
||||
} else {
|
||||
// by doing so, no async updater is available anymore
|
||||
// luckily I don't need them :)
|
||||
let [updater, callback] = args
|
||||
if (typeof updater === 'function') {
|
||||
updater = updater(instance.state, instance.props)
|
||||
}
|
||||
instance.setState(updater, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return wrappedMethods
|
||||
}
|
||||
|
||||
export default function connect(mapping) {
|
||||
return function linkComponent(ComponentClass) {
|
||||
return class AwesomeApp extends React.PureComponent {
|
||||
static displayName = `Driven${ComponentClass.name}`
|
||||
|
||||
state = {}
|
||||
boundCore = link(this, mapping)
|
||||
|
||||
render() {
|
||||
return <ComponentClass {...this.props} {...this.boundCore} {...this.state} />
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
src/driver/connect.ts
Normal file
118
src/driver/connect.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import * as React from 'react'
|
||||
|
||||
export type ParametersOfReturnedFunction<Func> = Func extends (<Args extends []>(
|
||||
...args: any[]
|
||||
) => (...args2: Args) => any)
|
||||
? Args
|
||||
: never
|
||||
|
||||
export type Method = (...args: Args) => void | Promise<void>
|
||||
type Args = any[] // This had to be any
|
||||
export type Middleware = <M extends Method, MM extends Method>(
|
||||
method: M,
|
||||
args: Parameters<M>
|
||||
) => [MM | M, Parameters<MM | M>]
|
||||
|
||||
const middlewares: Middleware[] = []
|
||||
|
||||
export function addMiddleware(middleware: Middleware) {
|
||||
if (typeof middleware !== 'function') return null
|
||||
const m = middleware.bind(null)
|
||||
middlewares.push(m)
|
||||
return function removeMiddleware() {
|
||||
const index = middlewares.indexOf(m)
|
||||
if (index === -1) return
|
||||
middlewares.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const applyMiddlewares: Middleware = function applyMiddlewares(method, args) {
|
||||
for (const middleware of middlewares) {
|
||||
;[method = method, args = args] = middleware(method, args)
|
||||
}
|
||||
return [method, args]
|
||||
}
|
||||
|
||||
function run<M extends Method>([method, args]: [M, Parameters<M>]) {
|
||||
method.apply(null, args)
|
||||
}
|
||||
|
||||
export type DispatchState<Props> = React.Component<Props, void>['setState']
|
||||
export type PreDispatch<Props, State> = (
|
||||
dispatchCallback: (props: Props, state: State) => Props | Promise<void> | void,
|
||||
callback?: () => void
|
||||
) => void
|
||||
export type TriggerOtherMethod = <MC extends MethodCreator>(
|
||||
methodCreator: MC,
|
||||
...args: ParametersOfReturnedFunction<MC>
|
||||
) => void
|
||||
|
||||
export type Dispatch<Props, State> = {
|
||||
state: DispatchState<Props>
|
||||
prepare: PreDispatch<Props, State>
|
||||
for: TriggerOtherMethod
|
||||
}
|
||||
|
||||
export type MethodCreator<Props = {}, State = any> = (dispatch: Dispatch<Props, State>) => Method
|
||||
|
||||
type Sources = {
|
||||
[key: string]: MethodCreator
|
||||
}
|
||||
type WrappedMethods = {
|
||||
[key: string]: Method
|
||||
}
|
||||
|
||||
function link<P, S>(instance: React.Component<P, S>, sources: Sources): WrappedMethods {
|
||||
const wrappedMethods: WrappedMethods = {
|
||||
/* [keyof sources] -> wrappedMethods.method */
|
||||
}
|
||||
const map = new Map<MethodCreator, Method>(/* sources.creator -> wrappedMethods.method */)
|
||||
|
||||
const dispatchState: DispatchState<P> = (updater, callback) => {
|
||||
instance.setState(updater, callback)
|
||||
}
|
||||
const prepareState: PreDispatch<P, S> = updater => {
|
||||
updater(instance.props, instance.state)
|
||||
}
|
||||
const dispatch = {
|
||||
for(createMethod: MethodCreator, ...otherArgs: ParametersOfReturnedFunction<MethodCreator>) {
|
||||
const isFromSource = sourcesValues.includes(createMethod)
|
||||
if (isFromSource) {
|
||||
const method = map.get(createMethod)
|
||||
const runnable = applyMiddlewares(method, otherArgs)
|
||||
run(runnable)
|
||||
}
|
||||
},
|
||||
prepare: prepareState,
|
||||
state: dispatchState,
|
||||
}
|
||||
|
||||
Object.entries(sources).forEach(([key, createMethod]) => {
|
||||
const method = createMethod(dispatch)
|
||||
wrappedMethods[key] = method
|
||||
map.set(createMethod, method)
|
||||
})
|
||||
|
||||
const sourcesValues = Object.values(sources)
|
||||
|
||||
return wrappedMethods
|
||||
}
|
||||
|
||||
export default function connect<BaseP, ExtraP>(mapping: Sources) {
|
||||
return function linkComponent<S>(
|
||||
ComponentClass: React.ComponentClass<BaseP & ExtraP, S>
|
||||
): React.ComponentClass<BaseP, ExtraP> {
|
||||
return class AwesomeApp extends React.PureComponent<BaseP, ExtraP> {
|
||||
static displayName = `Connected(${ComponentClass.name})`
|
||||
static defaultProps = ComponentClass.defaultProps
|
||||
|
||||
state = {} as ExtraP
|
||||
boundCore = link<BaseP, ExtraP>(this, mapping) as WrappedMethods
|
||||
|
||||
render() {
|
||||
const props = Object.assign({}, this.props, this.boundCore, this.state)
|
||||
return React.createElement(ComponentClass, props)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue