Sigi framework document
Introduction
Basic
ConceptActionEffectsDependencies InjectionModuleService
Recipes
FAQ
基本概念
深入
常见问题

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 {}
}