在开发异步加载的功能时,为提高用户体验一般会显示加载提示,最近在使用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 (
{isLoading ? 加载中...
: {count}
}
)
}
export default connect((state) => ({ ...state.count, loading: state.loading }))(Count)
我们可以通过 state.loading
判断组件的 model
甚至 effect
的状态。
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 (
{isLoading ? loading
: {count.count}
}
)
}
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 (
)
}
参考: