我们在项目中使用DVA + TS的时候有如下痛点
dispatch的type无法保证书写的绝对正确- model里的
effect的put同样无法保证type的书写正确
// model里的代码
const model = {
namespace: 'index',
reducers: {
dateState(state, {payload}) {
return {...sate, ...payload}
}
}
}
// 组件内部代码
dispatch({
type: 'indax/dataState',
payload: {
// ...
}
})
- 在
effect里通过return返回的是一个promise,但是在组件内使用.then去调用的时候,总是提示没有then方法
⚠️⚠️⚠️⚠️: 以下代码基于 Taro + Dva + Ts 的项目,有些类型无法直接从Dva中导入。例如:Model,Effect。如果可以,直接从Dva导入即可。
解决痛点1和3: 重写Dispatch类型
- 我们可以通过重写Dispatch的类型,来使编辑器的提示更加智能
// 文件目录: src/type/index.ts
import {Action, Reducer, Dispatch} from "redux";
/**
* ActionType, 推导当前effect & reducer
* @default string
*/
type ActionType<M extends Model | string> = M extends Model
? `${M['namespace']}/${(keyof M['effects'] | keyof M['reducers']) & string}`
: string;
type DispatchType = <S>(action: {
type: ActionType<S extends Model ? S : string>,
payload?: any
[key: string]: any
}) => Promise<any>
export type DvaDispatch = DispatchType & Dispatch
// interface DvaDispatch extends DvaDispatchType, Dispatch {}
- 使用方法
indexModel是model的类型定义,等下会说。
// 代码目录: src/index/index
import {connect} from 'react-redux';
import type {indexModel} from "./models";
import type {DvaDispatch} from "@/type";
const Index: React.FunctionComponent<{
dispatch: DvaDispatch
}> = ({ dispatch }) => {
const testDispatch = () => dispatch<indexModel>({
type: 'index/updateState'
})
return (
<View>
<Text onClick={testDispatch}>点我无事发生</Text>
</View>
)
}
const mapStateToProps = (state: allModel) => {
return { //... }
}
export default connect(mapStateToProps)(Index)
解决痛点2: 重写Model类型
- 仍然是通过重写Model类型的方式来使编辑器提示更加智能
- 因为在Taro里无法从Dva里导入Modal,所以直接把Modal的类型抄过来了
// 文件目录: src/type/index.ts
export type Effect = (action: AnyAction, effects: EffectsCommandMap) => void;
interface AnyAction extends Action {
[extraProps: string]: any
}
interface EffectsCommandMap {
put: <S>(action: {
type: EffectAction<S extends Model ? S : string>,
payload?: any
[key: string]: any
}) => any,
call: Function,
select: Function,
take: Function,
cancel: Function,
[key: string]: any,
}
type EffectAction<M extends Model | string> = M extends Model
? `${M['namespace']}/${(keyof M['effects'] | keyof M['reducers']) & string}` | `${(keyof M['effects'] | keyof M['reducers']) & string}`
: string;
export interface Model {
namespace: string,
state?: any,
reducers?: ReducersMapObject | ReducersMapObjectWithEnhancer,
effects?: EffectsMapObject,
subscriptions?: SubscriptionsMapObject,
}
type ReducersMapObject<S = any, A extends Action = Action> = {
[K in keyof S]: Reducer<S[K], A>
}
type ReducersMapObjectWithEnhancer = [ReducersMapObject, ReducerEnhancer];
interface ReducerEnhancer {
(reducer: Reducer<any>): void,
}
interface EffectsMapObject {
[key: string]: Effect | EffectWithType,
}
type EffectWithType = [Effect, { type: EffectType }];
type EffectType = 'takeEvery' | 'takeLatest' | 'watcher' | 'throttle';
interface SubscriptionsMapObject {
[key: string]: Subscription,
}
type Subscription = (api: SubscriptionAPI, done: Function) => void;
interface SubscriptionAPI {
history: History,
dispatch: DvaDispatch,
}
- 使用方式
// 文件目录: src/index/model/index
import { Reducer } from "redux";
import type {Effect, Model} from "@/type";
export interface indexModel extends Model{
namespace: 'index';
state: modelState;
reducers: {
updateState: Reducer<modelState>
}
effects: {
getStudentsByWxId: Effect
checkSchoolSet: Effect
}
}
interface modelState {}
const Index: indexModel = {
namespace: 'index',
state: {},
reducers: {
updateState(state, { payload }) {
return { ...state, ...payload }
},
},
effects: {
*getStudentsByWxId({ payload }, { put }) {
yield put<indexModel>({
type: 'updateState',
payload: {//。。。},
})
return `我返回了一个promise`
}
}
}
export default Index