• dva详解 State、View、Action、connect、dispatch、Reducer


    React的通信问题

    React 本身只是一个 DOM 的抽象层,使用组件来构建虚拟的 DOM。在开发大的应用的时候,则面临着如下需要解决的问题:

    • 通信:组件之间如何通信
    • 数据流:
      • 数据和视图如何串联起来?
      • 路由和数据如何绑定?
      • 如何异步编写逻辑?
      • ……

    组件通信

    组件之间会发生三种通信:

    • 向子组件发消息
    • 向父组件发消息
    • 向其他组件发消息

    对此 React 只提供了一种通信手段:传参,对于大应用通信来说很不方便。

    组件通信例子

    import React from 'react';
    import './style.css';
    
    export default function App() {
      return (
        <div>
          <Father />
        </div>
      );
    }
    
    class Son extends React.Component {
      render() {
        return <input />;
      }
    }
    
    class Father extends React.Component {
      render() {
        return (
          <div>
            <Son />
            <p>这里显示Son组件的内容</p>
          </div>
        );
      }
    }
    
    
    • 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

    对于这个例子,父组件该如何拿到子组件的值?

    import React from 'react';
    import './style.css';
    
    export default function App() {
      return (
        <div>
          <Father />
        </div>
      );
    }
    
    class Son extends React.Component {
      render() {
        return <input onChange={this.props.onChange} />;
      }
    }
    
    class Father extends React.Component {
      constructor() {
        super();
        this.state = {
          son: '',
        };
      }
    
      changeHandler(e) {
        this.setState({
          son: e.target.value,
        });
      }
    
      render() {
        return (
          <div>
            <Son onChange={this.changeHandler.bind(this)} />
            <p>这里显示Son组件的内容: {this.state.son}</p>
          </div>
        );
      }
    }
    
    
    • 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

    在这里插入图片描述

    数据流问题

    目前流行的数据流方案:

    • Flux ,单向数据流方案,以 Redux 为代表
    • Reactive , 响应式数据流方案,以 Mobx 为代表
    • 其他,如 rxjs 等

    对于 React 来说,最流行的社区 React 应用架构方案如下:

    • 路由: React-Router
    • 架构:Redux
    • 异步操作:Redux-saga

    缺点是需要引入多个库,项目结构复杂

    dva

    dva 是一个 React 应用框架,将上面三个 React 工具库包装在一起,简化了 API ,让开发更加方便快捷。

    dva = React-Router + Redux + Redux-saga

    dva 应用的结构

    import dva from 'dva';
    const App = () => <div>Hello dva</div>;
    
    // 创建应用
    const app = dva();
    
    // 注册视图
    app.router(() => <App />);
    
    // 启动应用
    app.start('#root');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    数据流图

    在这里插入图片描述

    核心概念

    • State:一个对象,保存整个应用状态
    • View:React 组件构成的视图层
    • Action:一个对象,描述事件
    • connect 方法:一个函数,绑定 State 到 View
    • dispatch 方法:一个函数,发送 Action 到 State

    以下部分可以结合Redux进行阅读

    State 和 View

    State 是储存数据的地方,收到 Action 以后,会更新数据。

    View 就是 React 组件构成的 UI 层,从 State 取数据后,渲染成 HTML 代码。只要 State 有变化,View 就会自动更新。

    Aciton

    Action 是用来描述 UI 层事件的一个对象。

    {
      type: 'click-submit-button',
      payload: this.form.data
    }
    
    • 1
    • 2
    • 3
    • 4

    connect 方法

    connect 是一个函数,绑定 State 到 View。

    import { connect } from 'dva';
    
    function mapStateToProps(state) {
      return { todos: state.todos };
    }
    connect(mapStateToProps)(App);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。

    connect 方法传入的第一个参数是 mapStateToProps 函数,mapStateToProps 函数会返回一个对象,用于建立 State 到 Props 的映射关系。

    dispatch 方法

    dispatch 是一个函数方法,用来将 Action 发送给 State。

    dispatch({
      type: 'click-submit-button',
      payload: this.form.data
    })
    
    • 1
    • 2
    • 3
    • 4

    dispatch 方法从哪里来?被 connect 的 Component 会自动在 props 中拥有 dispatch 方法。

    Reducer

    Reducer 是 Action 处理器,用来处理同步操作,可以看做是 state 的计算器。它的作用是根据 Action,从上一个 State 算出当前 State。

    // count +1
    function add(state) {
      return state + 1;
    }
    
    // 往 [] 里添加一个新 todo
    function addTodo(state, action) {
      return [...state, action.payload];
    }
    
    // 往 { todos: [], loading: true } 里添加一个新 todo,并标记 loading 为 false
    function addTodo(state, action) {
      return {
        ...state,
        todos: state.todos.concat(action.payload),
        loading: false,
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Effect

    Action 处理器,处理异步动作,基于 Redux-saga 实现。Effect 指的是副作用。根据函数式编程,计算以外的操作都属于 Effect,典型的就是 I/O 操作、数据库读写。

    function *addAfter1Second(action, { put, call }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Generator 函数

    Effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)。

    call 和 put

    dva 提供多个 effect 函数内部的处理函数,比较常用的是 call 和 put。

    • call:执行异步函数
    • put:发出一个 Action,类似于 dispatch

    dva应用的最简结构示例(含 model )

    import dva, { connect } from 'dva';
    
    const App = () => <div>Hello dva</div>;
    
    // 创建应用
    const app = dva();
    
    // 注册Model
    app.model({
      namespace: 'count',
      state: 0,
      reducers: {
        add(state) {
          return state + 1;
        },
      },
      effects: {
        *addAfter1Second(action, { call, put }) {
          yield call(delay, 1000);
          yield put({ type: 'add' });
        },
      },
    });
    // 注册视图
    app.router(() => <App />);
    
    // 启动应用
    app.start('#root');
    
    • 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

    数据流图

    在这里插入图片描述
    在这里插入图片描述

    app.model

    dva 提供 app.model 这个对象,所有的应用逻辑都定义在它上面。

    const app = dva();
    
    // 新增这一行
    app.model({ /**/ });
    
    app.router(() => <App />);
    app.start('#root');
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Model

    {
      namespace: 'count',
      state: 0,
      reducers: {
        add(state) { return state + 1 },
      },
      effects: {
        *addAfter1Second(action, { call, put }) {
          yield call(delay, 1000);
          yield put({ type: 'add' });
        },
      },
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    属性

    • namespace: 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
    • state: 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
    • reducers: Action 处理器,处理同步动作,用来算出最新的 State
    • effects:Action 处理器,处理异步动作

    实战案例

    写一个列表,包含删除按钮,点删除按钮后延迟 1 秒执行删除。
    在这里插入图片描述
    以下代码写在index.js

    1. 创建应用

    import React from 'react';
    import dva, { connect } from 'dva';
    
    // 1. 创建应用
    const app = dva();
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 注册Model

    import React from 'react';
    import dva, { connect } from 'dva';
    
    // 1. 创建应用
    const app = dva();
    
    // 2. 注册Model
    app.model({
      namespace: 'todoList',
      state: [
        {
          id: 1,
          name: 'list1',
        },
        {
          id: 2,
          name: 'list2',
        },
      ],
      reducers: {
        delete(todoList, action) {
          return todoList.filter((item) => item.id !== action.payload.id);
        },
      },
    });
    
    • 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

    reducers 中的 delete 方法,传入两个参数,前一个为 state 值,后一个为 action 操作

    3. 创建View

    import React from 'react';
    import dva, { connect } from 'dva';
    
    // 1. 创建应用
    const app = dva();
    
    // 2. 注册Model
    app.model({
      namespace: 'todoList',
      state: [
        {
          id: 1,
          name: 'list1',
        },
        {
          id: 2,
          name: 'list2',
        },
      ],
      reducers: {
        delete(todoList, action) {
          return todoList.filter((item) => item.id !== action.payload.id);
        },
      },
    });
    // 3. 创建View
    const App = connect(({ todoList }) => ({
      todoList,
    }))(function view(props) {
      return (props.todoList || []).map((item) => {
        return (
          <div>
            <h2>{item.name}</h2>
            <button
              onClick={() => {
                props.dispatch({
                  type: 'todoList/delete',
                  payload: item,
                });
              }}
            >
              删除
            </button>
          </div>
        );
      });
    });
    
    • 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

    4. 注册视图

    import React from 'react';
    import dva, { connect } from 'dva';
    
    // 1. 创建应用
    const app = dva();
    
    // 2. 注册Model
    app.model({
      namespace: 'todoList',
      state: [
        {
          id: 1,
          name: 'list1',
        },
        {
          id: 2,
          name: 'list2',
        },
      ],
      reducers: {
        delete(todoList, action) {
          return todoList.filter((item) => item.id !== action.payload.id);
        },
      },
    });
    
    // 3. 创建View
    const App = connect(({ todoList }) => ({
      todoList,
    }))(function view(props) {
      return (props.todoList || []).map((item) => {
        return (
          <div>
            <h2>{item.name}</h2>
            <button
              onClick={() => {
                props.dispatch({
                  type: 'todoList/delete',
                  payload: item,
                });
              }}
            >
              删除
            </button>
          </div>
        );
      });
    });
    
    // 4. 注册视图
    app.router(() => <App />);
    
    • 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

    5. 启动应用

    import React from 'react';
    import dva, { connect } from 'dva';
    
    // 1. 创建应用
    const app = dva();
    
    // 2. 注册Model
    app.model({
      namespace: 'todoList',
      state: [
        {
          id: 1,
          name: 'list1',
        },
        {
          id: 2,
          name: 'list2',
        },
      ],
      reducers: {
        delete(todoList, action) {
          return todoList.filter((item) => item.id !== action.payload.id);
        },
      },
    });
    
    // 3. 创建View
    const App = connect(({ todoList }) => ({
      todoList,
    }))(function view(props) {
      return (props.todoList || []).map((item) => {
        return (
          <div>
            <h2>{item.name}</h2>
            <button
              onClick={() => {
                props.dispatch({
                  type: 'todoList/delete',
                  payload: item,
                });
              }}
            >
              删除
            </button>
          </div>
        );
      });
    });
    
    // 4. 注册视图
    app.router(() => <App />);
    
    // 5. 启动应用
    app.start('#root');
    
    
    • 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

    在这里插入图片描述

  • 相关阅读:
    【C语言数据结构】线性表-顺序存储-动态分配(顺序表)
    Pandas数据重塑与透视
    家政服务小程序,家政预约小程序,家政服务预约小程序源码
    ROS2的launch有何不同?
    Windows 10 没有【休眠】选项的配置操作
    SAS|format&proc tabulate
    美团一面败在Redis,熬夜整理Redis 面试中常见的题目(附答案),3个月后卷土重来,成功拿下Offer!
    初识OpenGL (-)数学基础
    Topaz DeNoise AI 3.7 人工智能降噪
    机器学习(三十四):可视化决策树的四个方法
  • 原文地址:https://blog.csdn.net/weixin_43651049/article/details/127419290