Carmineprince's Blog
首页
    • HTML
    • CSS
    • JavaScript
    • Vue
    • React
    • TypeScript
    • Node
    • Flutter
    • Electron
    • Python
    • 运维
    • 重学前端
  • 分类
  • 标签
  • 归档

Ziqi Wang

胡思乱想程序员
首页
    • HTML
    • CSS
    • JavaScript
    • Vue
    • React
    • TypeScript
    • Node
    • Flutter
    • Electron
    • Python
    • 运维
    • 重学前端
  • 分类
  • 标签
  • 归档
  • 用TypeScript编写React的最佳实践
  • 项目结构搭建-慕课笔记
  • react17+ts+umi构建移动端web项目
  • CssModule同元素使用多个ClassName
  • 如何优雅的修改antd中的默认样式
  • React+Umi3的数据流解决方案-dva
    • 前言
    • Umi3
    • dva
      • @umijs/plugin-dva配置方式
      • 约定式的model组织方式
      • model文件--auth.ts文件
      • 组件中使用--login.tsx文件
      • subscription--订阅
  • Module "xxx" does not exist in container
  • React Hook详解
  • React相关
carmineprince
2022-02-17

React+Umi3的数据流解决方案-dva

# React+Umi3的数据流解决方案-dva

# 前言

最近初学React,在使用Umi构建项目的过程中,发现官方文档及百度上的各种资料,要么已经过时,要么需要很多的基础。特此记录下我个人对于dva的一些实际使用和理解。以下内容仅作为对官方文档的个人理解,文档有的,复制粘贴没有意义。

# Umi3

Umi3文档地址 (opens new window)

读了官网对Umi的介绍,感觉还是云里雾里。我个人对Umi的理解是类似于Vue cli脚手架的项目工具,可以通过配置来获得相应的扩展。目前的体验还是很舒服的。

# dva

社区维护的数据持久层工具,类似VueX,感觉很相似。

# @umijs/plugin-dva配置方式

需要手动开启

/config/config.ts或.umirc.ts中开启配置

dva: {}
1

# 约定式的model组织方式

所有符合以下规则的文件都会默认是model文件

  • scr/models下的文件
  • src/pages下,子目录中models目录下的文件
  • src/pages下,所有model.ts文件

总结就是,models文件夹的文件和model.ts文件

# model文件--auth.ts文件

import { Effect, Reducer } from 'umi';
import Request from '@/request/request';
import { auth as AuthApi } from '@/api/index';
import { Toast } from 'antd-mobile';

// 模型状态接口,需要储存什么数据
export interface AuthModelState {
  uuid: string;
  img: string;
}

// 给状态一个默认值
const initialState = {
  uuid: '',
  img: '',
};

// 声明模型的类型
export interface AuthModelType {
// 命名空间,可写可不写,不写的话此模型默认是文件名
  namespace: 'auth';
//   状态树
  state: AuthModelState;
//   异步操作用effects:发送异步请求,定时任务等。需要通过reducers修改状态
  effects: {
    code: Effect;
    login: Effect;
  };
//   同步操作,唯一可以直接修改状态的地方
  reducers: {
    saveCode: Reducer<AuthModelState>;
  };
}

// 模型
const AuthModel: AuthModelType = {
  namespace: 'auth',
  state: initialState,
  effects: {
    // 使用Generator函数语法来进行异步操作
    // 第一个参数是payload,里面有传递的数据
    // 第二个参数call,执行异步方法
    // 第三个参数put,执行reducers方法
    *code({ payload }, { call, put }) {
      // 发送请求方法
      const getCodeRequest = async () => {
        const res = await Request.get(AuthApi.code);
        return res;
      };
      // 执行异步方法
      const result = yield call(getCodeRequest);
      // 通过put执行reducers方法更新state
      yield put({
          // type不用写'auth/saveCode',默认是会在本model中找reducers的
        type: 'saveCode',
          // 传参
        payload: {
          ...result,
        },
      });
    },
    *login({ payload }, { call, put }) {
      const loginRequest = async () => {
        const res = await Request.post(AuthApi.login, { ...payload });
        return res;
      };
      const result = yield call(loginRequest);
      if (result.code !== 200) {
        Toast.show({
          content: result.msg,
        });
      }
    },
  },
  reducers: {
    saveCode(state = initialState, action) {
      return {
        ...action.payload,
      };
    },
  },
};
// 注意导出模型
export default AuthModel;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

# 组件中使用--login.tsx文件

// ...
import { ConnectProps, Loading, connect } from 'umi';
import { AuthModelState } from '@/models/auth';

// 从ConnectProps继承,创建本页的Props接口
// ConnectProps中有dispatch方法,用来执行effects或reducers函数
interface PageProps extends ConnectProps {
  auth: AuthModelState;
  loading: boolean;
}

// 这里的loading就是执行异步时会自动获取到是否加载中状态,会同步更新不用再手动设置和调整了。
const Login: React.FC<PageProps> = ({ auth, loading, dispatch }) => {
  // ...
  // 更新验证码
  const updateCode = () => {
    if (dispatch) {
      dispatch({
        type: 'auth/code',
      });
    }
  };
  // 点击了确认登录
  const onConfirm = () => {
    const params = {
      code,
      username: userName,
      password,
      uuid,
    };
    if (dispatch) {
      dispatch({
        type: 'auth/login',
        payload: params,
      });
    }
  };
  // ...
}

// 定义关联参数的类型
type ConnectType = {
  auth: AuthModelState;
  loading: Loading;
};
export default connect(({ auth, loading }: ConnectType) => ({
  auth,
  loading: loading.models.auth, // 在models中寻找需要绑定的异步loading。
}))(Login);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# subscription--订阅

model中的subscription相当于一个监听器,可以监听路由变化,鼠标,键盘变化,服务器连接变化,状态变化等,这样在其中就可以根据不同的变化做出相应的处理,在这个subsription中的方法名是随意定的,每次变化都会一次去调用里面的所有方法,所以一边会加相应的判断。

export default {
  namespace: 'example',
  state: {},
  subscriptions: {
    // 方法名随意命名,当监听有变化时会依次执行这的变化,
    setup({ dispatch, history }) {
      window.onresize = () => {
        // save是reducer的方法
        dispatch( type: 'save' )
      }
    },
    onClick({ dispatch }) {
      document.addEventListener('click', () => {
        dispatch( type: 'save' )
      })
    }
  },
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#umi3#react#dva#数据流转
上次更新: 2/17/2022, 3:13:46 PM
如何优雅的修改antd中的默认样式
Module "xxx" does not exist in container

← 如何优雅的修改antd中的默认样式 Module "xxx" does not exist in container→

最近更新
01
pc端rem配置
03-02
02
使用动态变量ts报错的解决
02-25
03
React Hook详解
02-18
更多文章>
Theme by Vdoing | Copyright © 2021-2022 Carmineprince | 鲁ICP备2021046263号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式