Dependencies Injection
If you are familiar with Angular or NestJS, you may already be familiar with Dependencies Injection.
Module
In Sigi
world, every EffectModule is Injectable. So you can easily combine modules by Inject one to the others.
@Module("A")class ModuleA extends EffectModule<AState> {defaultState = {};}@Module("B")class ModuleB extends EffectModule<BState> {defaultState = {};constructor(private readonly moduleA: ModuleA) {super();}}
Access AppState
from the other EffectModule
Unlike Redux, Sigi
does not have Global Store object to store the whole state in application.
But with Dependencies Injection, we still can access state from the other parts in our application:
@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]) => {...}))}}
Trigger actions of the other effect modules
Just be similar with Emit actions in Effect using ActionsCreator, we can create Action
using ActionsCreator in the other EffectModule class
.
@Module("A")class ModuleA extends EffectModule<AState> {defaultState = {};@ImmerReducer()set(state: Draft<AState>, payload: string) {state.syncFromB = payload;}}@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]) => {return this.moduleA.getActions().set(`${stateA.syncFromB}${payload}`);}));}}
Service
If you want create a pure Service which separated from EffectModule class
, you can just decorate it by Injectable
decorator from Sigi
.
@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 {}}