前言
最近使用umi3+dva开发项目,按照官方示例虽然使用了ts,但使用起来对ts的支持不太完善,鄙人ts也就小白水平,就摸索着优化了一下。
正常使用
约定式model:src/models/global.ts
import { GlobalModelType } from "@/typings/models/global"
const GlobalModel: GlobalModelType = {
namespace: 'global', // 表示在全局 state 上的 key
state: {
userInfo: {}, // 用户信息
token: localStorage.getItem('token') || '',
},
reducers: { // 管理同步方法,必须是纯函数
// 登录信息
setUserInfo(state, action) {
state.userInfo = JSON.parse(JSON.stringify(action.payload));
localStorage.setItem('userInfo', JSON.stringify(action.payload));
},
// 存储token
setToken(state, action) {
state.token = action.payload;
localStorage.setItem('token', action.payload);
},
},
effects: {
}, // 管理异步操作,采用了 generator 的相关概念
subscriptions: {}, // 订阅数据源
};
export default GlobalModel;
globalModel的声明文件: src/typings/models/global.d.ts
这里的声明文件是完全按照官方的方式来写的,也贴出来防止遗漏。
import type { ImmerReducer } from 'umi';
export type GlobalModelState = {
userInfo: Record<string, any>
token: string;
}
export type GlobalModelType = {
namespace: string;
state: GlobalModelState;
effects: {
// query: Effect;
};
reducers: {
// 启用 immer 之后
setUserInfo: ImmerReducer<GlobalModelState>;
setToken: ImmerReducer<GlobalModelState>;
};
subscriptions: any;
}
问题
由于我不喜欢connect的连接方式,所以都是使用hooks的方式,注意在dva2.6.x有效。
按照官方使用方法,发现了如下问题,去翻阅了Issues发现也有人提这些问题,就知道不是我操作问题了。
1.dispatch 无提示
// 基础使用方法
import { useDispatch } from 'umi';
const dispatch = useDispatch();
const demo = () => {
dispatch({
type: 'global/setXXX', //指定哪个 model 层里面的哪个 方法
payload: {
...
}
//需要传递到 model 层里面的参数。
});
};
2.useSelector 报“类型“DefaultRootState”上不存在属性‘xxx’ ”
这个估计大家都遇到过了,至于为什么不用useStore。因为不是响应式的在某些场景下就没有使用,还为此踩了坑。
当然你也可以直接 const store = useSelector((state: any) => state.global) any大法解君愁。
解决方案
没有好办法,只能自己写一套... 其实代码还算简洁的(其实还能更简洁)。
1.建立新xx.d.ts文件用于扩展声明,建议直接declare声明。
import { GlobalModelState, GlobalModelType } from './global'
import { MenuModelState, MenuModelType } from './menu'
declare namespace DVA {
/** DVA模块名称声明 */
type Models = {
global: GlobalModelState;
menu: MenuModelState;
}
/** DVA Action声明 */
type Action<T = any> = (action:
{
type: `global/${keyof (GlobalModelType['reducers'] &
GlobalModelType['effects'])}`;
payload?: T;
[key: string]: any;
} |
{
type: `menu/${keyof (MenuModelType['reducers'] &
MenuModelType['effects'])}`;
payload?: T;
[key: string]: any;
}
) => any;
}
export = DVA
export as namespace DVA
2.使用方式
import { useDispatch, useSelector } from 'umi';
const Demo = () => {
const dispatch: DVA.Action = useDispatch();
const store = useSelector((state: DVA.Models) => state)
const { userInfo } = store.global
}
3.实现效果
问题1解决
问题2解决且有了提示