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 { useEffectModule } from '@sigi/react'
import { initDevtool } from '@sigi/devtool'
import { Modal } from 'antd'
import { AppModule } from './app.module'
function App() {
const [{ list }, dispatcher] = useEffectModule(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()