优化Dva+Ts代码提示

562 阅读2分钟

前言

最近使用umi3+dva开发项目,按照官方示例虽然使用了ts,但使用起来对ts的支持不太完善,鄙人ts也就小白水平,就摸索着优化了一下。

文档:v3.umijs.org/zh-CN/plugi…

正常使用

约定式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 无提示

image.png

// 基础使用方法
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大法解君愁。

image.png

解决方案

没有好办法,只能自己写一套... 其实代码还算简洁的(其实还能更简洁)。

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解决

image.png

问题2解决且有了提示

image.png

image.png