zoukankan      html  css  js  c++  java
  • 003-and design-dva.js 知识导图-02-Reducer,Effect,Subscription,Router,dva配置,工具

    一、Reducer

    reducer 是一个函数,接受 state 和 action,返回老的或新的 state 。即:(state, action) => state

    增删改

    以 todos 为例。

    app.model({
      namespace: 'todos',
      state: [],
      reducers: {
        add(state, { payload: todo }) {
          return state.concat(todo);
        },
        remove(state, { payload: id }) {
          return state.filter(todo => todo.id !== id);
        },
        update(state, { payload: updatedTodo }) {
          return state.map(todo => {
            if (todo.id === updatedTodo.id) {
              return { ...todo, ...updatedTodo };
            } else {
              return todo;
            }
          });
        },
      },
    };

    嵌套数据的增删改

    建议最多一层嵌套,以保持 state 的扁平化,深层嵌套会让 reducer 很难写和难以维护。

    app.model({
      namespace: 'app',
      state: {
        todos: [],
        loading: false,
      },
      reducers: {
        add(state, { payload: todo }) {
          const todos = state.todos.concat(todo);
          return { ...state, todos };
        },
      },
    });

    下面是深层嵌套的例子,应尽量避免。

    app.model({
      namespace: 'app',
      state: {
        a: {
          b: {
            todos: [],
            loading: false,
          },
        },
      },
      reducers: {
        add(state, { payload: todo }) {
          const todos = state.a.b.todos.concat(todo);
          const b = { ...state.a.b, todos };
          const a = { ...state.a, b };
          return { ...state, a };
        },
      },
    });

    二、Effect

    示例:

    app.model({
      namespace: 'todos',
      effects: {
        *addRemote({ payload: todo }, { put, call }) {
          yield call(addTodo, todo);
          yield put({ type: 'add', payload: todo });
        },
      },
    });

    Effects

    put

    用于触发 action 。

    yield put({ type: 'todos/add', payload: 'Learn Dva' });

    call

    用于调用异步逻辑,支持 promise 。

    const result = yield call(fetch, '/todos');

    select

    用于从 state 里获取数据。

    const todos = yield select(state => state.todos);

    错误处理

    全局错误处理

    dva 里,effects 和 subscriptions 的抛错全部会走 onError hook,所以可以在 onError 里统一处理错误。

    const app = dva({
      onError(e, dispatch) {
        console.log(e.message);
      },
    });

    然后 effects 里的抛错和 reject 的 promise 就都会被捕获到了。

    本地错误处理

    如果需要对某些 effects 的错误进行特殊处理,需要在 effect 内部加 try catch 。

    app.model({
      effects: {
        *addRemote() {
          try {
            // Your Code Here
          } catch(e) {
            console.log(e.message);
          }
        },
      },
    });

    异步请求

    异步请求基于 whatwg-fetch,API 详见:https://github.com/github/fetch

    GET 和 POST

    import request from '../util/request';
    
    // GET
    request('/api/todos');
    
    // POST
    request('/api/todos', {
      method: 'POST',
      body: JSON.stringify({ a: 1 }),
    });

    统一错误处理

    假如约定后台返回以下格式时,做统一的错误处理。

    {
      status: 'error',
      message: '',
    }

    编辑 utils/request.js,加入以下中间件:

    function parseErrorMessage({ data }) {
      const { status, message } = data;
      if (status === 'error') {
        throw new Error(message);
      }
      return { data };
    }

    然后,这类错误就会走到 onError hook 里。

    三、Subscription

    subscriptions 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }) => unsubscribe 。

    异步数据初始化

    比如:当用户进入 /users 页面时,触发 action users/fetch 加载用户数据。

    app.model({
      subscriptions: {
        setup({ dispatch, history }) {
          history.listen(({ pathname }) => {
            if (pathname === '/users') {
              dispatch({
                type: 'users/fetch',
              });
            }
          });
        },
      },
    });

    path-to-regexp Package

    如果 url 规则比较复杂,比如 /users/:userId/search,那么匹配和 userId 的获取都会比较麻烦。这是推荐用 path-to-regexp 简化这部分逻辑。

    import pathToRegexp from 'path-to-regexp';
    
    // in subscription
    const match = pathToRegexp('/users/:userId/search').exec(pathname);
    if (match) {
      const userId = match[1];
      // dispatch action with userId
    }

    四、router

    Config with JSX Element (router.js)

    <Route path="/" component={App}>
      <Route path="accounts" component={Accounts}/>
      <Route path="statements" component={Statements}/>
    </Route>

    详见:react-router

    Route Components

    Route Components 是指 ./src/routes/ 目录下的文件,他们是 ./src/router.js 里匹配的 Component。

    通过 connect 绑定数据

    比如:

    import { connect } from 'dva';
    function App() {}
    
    function mapStateToProps(state, ownProps) {
      return {
        users: state.users,
      };
    }
    export default connect(mapStateToProps)(App);

    然后在 App 里就有了 dispatch 和 users 两个属性。

    Injected Props (e.g. location)

    Route Component 会有额外的 props 用以获取路由信息。

    • location
    • params
    • children

    更多详见:react-router

    基于 action 进行页面跳转

    import { routerRedux } from 'dva/router';
    
    // Inside Effects
    yield put(routerRedux.push('/logout'));
    
    // Outside Effects
    dispatch(routerRedux.push('/logout'));
    
    // With query
    routerRedux.push({
      pathname: '/logout',
      query: {
        page: 2,
      },
    });

    除 push(location) 外还有更多方法,详见 react-router-redux

    五、dva配置

    Redux Middleware

    比如要添加 redux-logger 中间件:

    import createLogger from 'redux-logger';
    const app = dva({
      onAction: createLogger(),
    });

    注:onAction 支持数组,可同时传入多个中间件。

    history

    切换 history 为 browserHistory

    import { browserHistory } from 'dva/router';
    const app = dva({
      history: browserHistory,
    });

    去除 hashHistory 下的 _k 查询参数

    import { useRouterHistory } from 'dva/router';
    import { createHashHistory } from 'history';
    const app = dva({
      history: useRouterHistory(createHashHistory)({ queryKey: false }),
    });

    六、工具

    通过 dva-cli 创建项目

    先安装 dva-cli 。

    $ npm install dva-cli -g

    然后创建项目。

    $ dva new myapp

    最后,进入目录并启动。

    $ cd myapp
    $ npm start
  • 相关阅读:
    HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第7章定位
    HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第6章表格与表单
    【原创】Linux虚拟化KVM-Qemu分析(一)
    Qt开发技术:QCharts(三)QCharts样条曲线图介绍、Demo以及代码详解
    2.MongoDB 4.2副本集环境基于时间点的恢复
    1.MongoDB 2.7主从复制(master –> slave)环境基于时间点的恢复
    离线人脸识别门禁考勤——Android设备端APK及源码免费下载
    Centos7 忘记密码的解决方法
    基于fastText模型的文本分类
    各种文本分类模型实践
  • 原文地址:https://www.cnblogs.com/bjlhx/p/9213862.html
Copyright © 2011-2022 走看看