react+ant.d添加全局loading方式
背景
先上个应景图,哈哈哈!
本篇博客中的方法也是前两天刚接触redux
时给了我的一点启发,可以从这里着手来实现这个功能。而且,事实证明,确实满足了我的需求。
项目开发中往往需要在接口返回较慢的时候给出loading
状态,防止在接口返回值的过程中用户多次点击,之前我们在Vue
中也添加过这个功能,而且还在其中遇到过由于单例模式出现的问题,以及如何解决这个问题大家可以自行百度。
Vue
中全局loading
组件直接通过创建实例的方式,在ant.d
中我们的Spin
组件需要包裹想要遮罩的元素,我们如何在请求接口的入口统一添加全局loading
呢?
这里我们就用到了redux
(react+redux超简单入门实例这里可以带你迅速了解并使用redux
)
使用redux实现全局Loading
添加Spin
首先在main
文件中添加全局Spin
,main.js
文件包裹了所有菜单栏所包含的内容(除了登录页,基本都包含在这里)
// main.js import React, { Component } from 'react' import AppMenu from '../../components/app-menu' import AppBreadCrum from '../../components/app-breadcrum' import { Switch, Route } from 'react-router-dom' import { mainRoutes } from '../../router' import { Spin } from 'antd' import store from '../../store' class Main extends Component { state = { loading: false } render () { const { layout, ...rest } = this.props let { loading } = this.state return ( <Spin spinning={loading} wrapperClassName="page-loading"> <div className="main"> <AppMenu {...rest}></AppMenu> <div className="app-right"> <AppBreadCrum {...rest}></AppBreadCrum> <div className="app-bottom"> <Switch> {mainRoutes.map(route => (<Route exact key={route.path} path={route.path} component={route.component}></Route>))} </Switch> </div> </div> </div> </Spin> ) } } export default Main
接口拦截设置Loading显示
我们需要在接口发出请求的时候设置Loading
显示,在返回以后设置其为隐藏。
actions
定义两个action
动作,一个用来打开loading
一个用来关闭loading
// actions/index.js export const OPENPAGELOADING = 'OpenPageLoading' export const CLOSEPAGELOADING = 'ClosePageLoading'
reducers
通过不同事件来触发值的改变
// reducers/index.js import { OPENPAGELOADING, CLOSEPAGELOADING } from '../actions' const initState = { pageLoadingVal: false } const AppReducer = (state=initState, action) => { switch (action.type) { case OpenPageLoading: { return { pageLoadingVal: true } } case ClosePageLoading: { return { pageLoadingVal: false } } default: { return state } } } export default AppReducer
store
// store/index.js import { createStore } from 'redux' import AppReducer from '../reducers' const store = createStore(AppReducer) export default store
引入
// index.js ... import store from './store' ReactDOM.render( <Provider store={store}> <ConfigProvider locale={antdZhCn}> <Router> <App /> </Router> </ConfigProvider> </Provider>, document.getElementById('root') )
http.js
接口入口文件中设置全局的值
import axios from 'axios' import { message } from 'antd' import store from '../store' import { OPENPAGELOADING, CLOSEPAGELOADING } from '../actions' let instance = axios.create({ baseURL: '', timeout: 5000 }) /* 请求拦截 */ instance.interceptors.request.use(config => { store.dispatch({type: OPENPAGELOADING}) return config }, error => { store.dispatch({type: CLOSEPAGELOADING }) message.error('请求超时') return Promise.reject(error) }) /* 响应拦截 */ instance.interceptors.response.use(response => { store.dispatch({type: CLOSEPAGELOADING }) let { data } = response if (data && data.code && data.code === 200) { return data } else if (data && data.code && data.code === 500) { message.error(data.msg || '获取接口数据错误') return Promise.reject() } }, error => { store.dispatch({type: CLOSEPAGELOADING }) message.error('服务错误') return Promise.reject(error) }) export default instance
在发出接口请求的时候通过store.dispatch({type: 'OpenPageLoading'})
派发操作
需要注意的时候,在无论接口返回是什么的情况下需要通过store.dispatch({type: 'ClosePageLoading'})
来关闭loading
。
监测store中loading值设置是否显示Spin
到此为止我们store
中的全局loading
的值已经发生了满足需求的改变,
下面要做的就是在每次值发生改变的时候我们能在使用Spin
的地方监听到,
说到监听是不是就想到了store
提供的subscribe
方法呢?怎么做呢?
// main.js import React, { Component } from 'react' import AppMenu from '../../components/app-menu' import AppBreadCrum from '../../components/app-breadcrum' import { Switch, Route } from 'react-router-dom' import { mainRoutes } from '../../router' import { Spin } from 'antd' import store from '../../store' class Main extends Component { state = { loading: false } componentDidMount () { // 重点 // 重点 // 重点 // 监听store中pageLoadingVal值 store.subscribe(() => { let storeState = store.getState() this.setState({ loading: storeState.pageLoadingVal }) }) } render () { const { layout, ...rest } = this.props let { loading } = this.state return ( <Spin spinning={loading} wrapperClassName="page-loading"> // ... </Spin> ) } } export default Main
是的,通过监听来设置当前组件的state
的值。
优化
如果你一个页面只有一个接口,那么你完成上述就搞定了。
但是,如果你一个页面有多个接口,当其中有个接口返回值很慢的时候你就会发现问题,就是当一个接口pending
结束全局loading
就消失了,这个当然不是我们想要的结果,理想的亚子是当前页面所有接口都pending
结束后才消失loading
。
这个问题之前在做elementUI+Vue
做全局loading
时候也遇到过,其实思路是一样的,思路可参考elementUI全局Loading单例模式。
因为创建全局loading
的方式不同,所以这里思路一样,代码稍有不同
// http.js import axios from 'axios' import { message } from 'antd' import store from '../store' import { OPENPAGELOADING, CLOSEPAGELOADING } from '../actions' let instance = axios.create({ baseURL: '', timeout: 5000 }) /* 添加一个计数器 */ let needLoadingRequestCount = 0 function showFullScreenLoading () { if (needLoadingRequestCount === 0) { store.dispatch({type: OPENPAGELOADING}) } needLoadingRequestCount++ } function tryHideFullScreenLoading () { if (needLoadingRequestCount <= 0) return needLoadingRequestCount-- if (needLoadingRequestCount === 0) { store.dispatch({type: CLOSEPAGELOADING}) } } /* 请求拦截 */ instance.interceptors.request.use(config => { showFullScreenLoading() return config }, error => { tryHideFullScreenLoading() message.error('请求超时') return Promise.reject(error) }) /* 响应拦截 */ instance.interceptors.response.use(response => { tryHideFullScreenLoading() let { data } = response if (data && data.code && data.code === 200) { return data } else if (data && data.code && data.code === 500) { message.error(data.msg || '获取接口数据错误') return Promise.reject() } }, error => { tryHideFullScreenLoading() message.error('服务错误') return Promise.reject(error) }) export default instance
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
最新评论