Sigi framework document
Introduction
Basic
Recipes
FAQType InferEffect end callback
基本概念
深入
常见问题

FAQ

Type Infer

When you are writing Effect/SSREffect logic, if the Effect emit an Action which infer from itself, or some Effects circular dependents the others, Type infer in TypeScript may fail.

Example in CodeSandbox

import { EffectModule, Module, ImmerReducer, Effect } from "@sigi/core";
import { Draft } from "immer";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
export interface CountState {
count: number;
}
@Module("Count")
export class CountModule extends EffectModule<CountState> {
defaultState = {
count: 0,
};
@ImmerReducer()
set(state: Draft<CountState>, payload: number) {
state.count = state.count + payload;
}
@Effect()
addLeastFive(payload$: Observable<number>) {
return payload$.pipe(
map((payload) => {
if (payload < 5) {
return this.getActions().addLeastFive(payload + 1);
}
return this.getActions().set(payload);
})
);
}
}

In this scenario, you need give the Effect a Return Type explicit:

- import { EffectModule, Module, ImmerReducer, Effect } from '@sigi/core'
+ import { EffectModule, Module, ImmerReducer, Effect, Action } from '@sigi/core'
- addLeastFive(payload$: Observable<number>) {
+ addLeastFive(payload$: Observable<number>): Observable<Action> {

Effect end callback

There are some cases you may need run callback after Effect end:

Show message after Success/Failure

Depends on the API which show message, we could use Data driven/Side effect ways to implement this requirement:

CodeSandbox effects callback side effect

import { Module, EffectModule, Reducer, Effect, Action } from "@sigi/core";
import { Observable, of } from "rxjs";
import {
exhaustMap,
takeUntil,
map,
tap,
startWith,
catchError,
} from "rxjs/operators";
import { message } from "antd";
import { HttpClient } from "./http.service";
interface AppState {
list: string[] | null | Error;
}
@Module("App")
export class AppModule extends EffectModule<AppState> {
defaultState: AppState = {
list: [],
};
constructor(private readonly httpClient: HttpClient) {
super();
}
@Reducer()
cancel(state: AppState) {
return { ...state, ...this.defaultState };
}
@Reducer()
setList(state: AppState, list: AppState["list"]) {
return { ...state, list };
}
@Effect()
fetchList(payload$: Observable<void>): Observable<Action> {
return payload$.pipe(
exhaustMap(() => {
return this.httpClient.get(`/resources`).pipe(
tap(
() => {
message.success("Got response");
},
(e) => {
message.error(e.message);
}
),
map((response) => this.getActions().setList(response)),
catchError((e) => of(this.getActions().setList(e))),
startWith(this.getActions().setList(null)),
takeUntil(this.getAction$().cancel)
);
})
);
}
}

CodeSandbox effects callback state driven

import "reflect-metadata";
import "antd/dist/antd.css";
import React, { useState, useCallback } from "react";
import { render } from "react-dom";
import { useModule } from "@sigi/react";
import { initDevtool } from "@sigi/devtool";
import { Modal } from "antd";
import { AppModule } from "./app.module";
function App() {
const [{ list }, dispatcher] = useModule(AppModule);
const [modalVisible, setModalVisible] = useState(true);
const onFetchList = useCallback(() => {
setModalVisible(true);
dispatcher.fetchList();
}, [dispatcher, setModalVisible]);
const onClose = useCallback(() => {
setModalVisible(false);
}, [setModalVisible]);
const loading = !list ? <div>loading</div> : null;
const title =
list instanceof Error ? (
<>
<Modal
title="fail"
visible={modalVisible}
onOk={onClose}
onCancel={onClose}
>
<p>{list.message}</p>
</Modal>
<h1>{list.message}</h1>
</>
) : (
<h1>Hello CodeSandbox</h1>
);
const listNodes = Array.isArray(list)
? list.map((value) => <li key={value}>{value}</li>)
: null;
return (
<div>
{title}
<button onClick={onFetchList}>fetchList</button>
<button onClick={dispatcher.cancel}>cancel</button>
{loading}
<ul>{listNodes}</ul>
</div>
);
}
const rootElement = document.getElementById("app");
render(<App />, rootElement);
initDevtool();