依赖注入
如果你用过 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()}}
访问其它 EffectModule 的 AppState
跟 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]) => {...}))}}
触发其它 EffectModule 的 Action
跟触发自己的 Action
一样,我们可以通过其它 EffectModule 的 ActionsCreator 来触发它们的 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 {}}