dva-loading使用总结

在开发异步加载的功能时,为提高用户体验一般会显示加载提示,最近在使用umi做项目时接触到dva-loading,对全局和局部组件的异步加载控制还是非常方便的。

在umi中使用

安装和配置

安装:

$ npm install dva-loading -S

进入 src/app.js 进行 运行时dva配置

import createLoading from "dva-loading"

export const dva = {
  plugins: [createLoading()]
}

models

models 文件夹下新建count.js,输入下面内容:

const delay = (ms)=>new Promise(r=>setTimeout(r,ms))

export default {
    namespace:"count",
    state:{
        count:1,
    },
    effects:{
        *add(action,{put,call}){
            yield call(delay,1000);
            yield put({type:"change",payload:Math.random()})
        }
    },
    reducers:{
        change(state,{payload}){
            return {count:state.count+payload}
        }
    }
}

组件中使用

新建 Count.js组件进行测试:

import React from "react"
import { connect } from "dva"

function Count({ dispatch, count, loading }) {
    
    const isLoading = loading.models.count;
    // 单独对 effects 控制
    // const isLoading = loading.effects["count/add"]
    // 对多个 effects 控制
    // const isLoading = loading.effects["count/add"] || loading.effects["count/minus"] || false;

  return (
    <div>
      {isLoading ? <p>加载中...</p> : <p>{count}</p>}
      <button onClick={() => dispatch({ type: "count/add" })}>+</button>
    </div>
  )
}

export default connect((state) => ({ ...state.count, loading: state.loading }))(Count)

我们可以通过 state.loading 判断组件的 model甚至 effect 的状态。

dva-loading 源码

dva-loading

const SHOW = '@@DVA_LOADING/SHOW';
const HIDE = '@@DVA_LOADING/HIDE';
const NAMESPACE = 'loading';

function createLoading(opts = {}) {
  const namespace = opts.namespace || NAMESPACE;

  const { only = [], except = [] } = opts;
  if (only.length > 0 && except.length > 0) {
    throw Error('It is ambiguous to configurate `only` and `except` items at the same time.');
  }

  const initialState = {
    global: false,
    models: {},
    effects: {},
  };

  const extraReducers = {
    [namespace](state = initialState, { type, payload }) {
      const { namespace, actionType } = payload || {};
      let ret;
      switch (type) {
        case SHOW:
          ret = {
            ...state,
            global: true,
            models: { ...state.models, [namespace]: true },
            effects: { ...state.effects, [actionType]: true },
          };
          break;
        case HIDE: {
          const effects = { ...state.effects, [actionType]: false };
          const models = {
            ...state.models,
            [namespace]: Object.keys(effects).some(actionType => {
              const _namespace = actionType.split('/')[0];
              if (_namespace !== namespace) return false;
              return effects[actionType];
            }),
          };
          const global = Object.keys(models).some(namespace => {
            return models[namespace];
          });
          ret = {
            ...state,
            global,
            models,
            effects,
          };
          break;
        }
        default:
          ret = state;
          break;
      }
      return ret;
    },
  };

  function onEffect(effect, { put }, model, actionType) {
    const { namespace } = model;
    if (
      (only.length === 0 && except.length === 0) ||
      (only.length > 0 && only.indexOf(actionType) !== -1) ||
      (except.length > 0 && except.indexOf(actionType) === -1)
    ) {
      return function*(...args) {
        yield put({ type: SHOW, payload: { namespace, actionType } });
        yield effect(...args);
        yield put({ type: HIDE, payload: { namespace, actionType } });
      };
    } else {
      return effect;
    }
  }

  return {
    extraReducers,
    onEffect,
  };
}

export default createLoading;

@umijs/plugin-dva 接口实现

@umijs/plugin-dva 抛出的 useSelector 方法可以很方便的帮助我们获取models 层数据:

const { loading, count } = useSelector((stores) => ({ 
      loading: stores.loading, 
      count: stores.count 
    }))

通过 useDispatch 获取 dispatch 方法:

const dispatch = useDispatch()
const add = () => dispatch({ type: "count/add" })

修改状态:

import React from "react"
import { useDispatch, useSelector } from "dva"

function Count(props) {
  const dispatch = useDispatch()
  const add = () => dispatch({ type: "count/add" })

  const { loading, count } = useSelector((stores) => ({ 
      loading: stores.loading, 
      count: stores.count 
    }))
  const isLoading = loading.models.count

  return (
    <div>
      {isLoading ? <p>loading</p> : <p>{count.count}</p>}
      <button onClick={add}>+</button>
    </div>
  )
}

export default Count

全局 loading 控制

通过 useSelector方法得到 stores.loading.global,判断 models是否在loading中:

import React from 'react'
const {useSelector} = 'dva'
import {Spin} from 'antd'
const DemoPage = () => {
  const {loading} = useSelector(stores => ({
    loading: stores.loading
  }))
  return (
    <Spin spinning={loading.global}/>
  )
}

参考: