Sigi framework document
Introduction
Basic
Recipes
FAQ
基本概念
概念ActionEffects依赖注入ModuleService
深入
常见问题

依赖注入

如果你用过 Angular 或者 NestJS, 你可能已经对 依赖注入 这个概念比较熟悉了。

Module

Sigi 程序中,所有的 EffectModule 都是可依赖注入的。所以你可以通过将一个 EffectModule 注入到另一个 EffectModule 里面来达到组合它们的目的。

@Module('A')
class ModuleA extends EffectModule<AState> {
defaultState = {}
}
@Module('B')
class ModuleB extends EffectModule<BState> {
defaultState = {}
constructor(private readonly moduleA: ModuleA) {
super()
}
}

访问其它 EffectModuleAppState

Redux 不一样的是,Sigi 没有 全局唯一 Store 来存储所有的状态。 但是在 依赖注入 的帮助下,我们依然可以在一个 EffectModule 中访问其它 EffectModule 的状态。

@Module('A')
class ModuleA extends EffectModule<AState> {
defaultState = {}
}
@Module('B')
class ModuleB extends EffectModule<BState> {
defaultState = {}
constructor(private readonly moduleA: ModuleA) {
super()
}
@Effect()
addAndSync(payload$: Observable<number>) {
return payload$.pipe(
withLatestFrom(this.moduleA.state$),
map(([payload, stateA]) => {
...
})
)
}
}

触发其它 EffectModuleAction

跟触发自己的 Action 一样,我们可以通过其它 EffectModuleActionsCreator 来触发它们的 Action:

@Module('A')
class ModuleA extends Ayanami<AState> {
defaultState = {}
@ImmerReducer()
set(state: Draft<AState>, payload: string) {
state.syncFromB = payload
}
}
@Module('B')
class ModuleB extends Ayanami<BState> {
defaultState = {}
constructor(private readonly moduleA: ModuleA) {
super()
}
@Effect()
addAndSync(payload$: Observable<number>) {
return payload$.pipe(
withLatestFrom(this.moduleA.state$),
map(([payload, stateA]) => {
return this.moduleA.getActions().set(`${stateA.syncFromB}${payload}`)
})
)
}
}

Service

如果你想创建一个纯(不包含状态)的 Service, 你可以用 Injectable 装饰这个 Service 类,这样你就可以将它注入到 EffectModule 里面了。

@Module('Simple')
class SimpleModule extends EffectModule<SimpleState> {
defaultState = {}
constructor(private readonly httpClient: HttpClient) {
super()
}
@Effect()
create(payload$: Observable<CreateEntityPayload>) {
return payload$.pipe(
withLatestFrom(this.state$),
exhaustMap(([payload, state]) => {
return this.httpClient.post(`/resources/${state.id}`, {
body: payload,
})
})
)
}
}
import { Injectable } from '@sigi/di'
import { Observable } from 'rxjs'
@Injectable()
export class HttpClient {
constructor(private readonly tracer: Tracer) {}
get () {}
post<T>(config: Config = {}): Observable<T> {
return this.send<T>({
...config,
method: 'POST',
})
}
delete() {}
put() {}
private send<T>(config: Config): Observable<T> {
return this.tracer.send(config)
}
}
import { Injectable } from '@sigi/di'
import { Observable } from 'rxjs'
export type TraceId = string & {
readonly traceIdTag: unique symbol
}
@Injectable()
export class Tracer {
send<T>(config: Config): Observable<T> {
const traceId = this.generateTraceId()
this.traceStart(traceId)
return new Observable<T>((observer) => {
const { config, abortController } = this.convertConfig(config, traceId)
fetch(config).then((res) => {
this.traceEnd(traceId, res)
return res.json()
}).then((data) => {
observer.next(data)
observer.complete()
}).catch((e) => {
observer.error(e)
})
return () => {
abortController.abort()
}
})
}
private convertConfig(config: Config, traceId: TraceId): { config: FetchInit, abortController: AbortController } {}
private traceStart(traceId: TraceId) {}
private traceEnd(traceId: TraceId, res: Response) {}
private generateTraceId(): TraceId {}
}