react+ant.d添加全局loading方式
背景
先上個(gè)應(yīng)景圖,哈哈哈!

本篇博客中的方法也是前兩天剛接觸redux時(shí)給了我的一點(diǎn)啟發(fā),可以從這里著手來(lái)實(shí)現(xiàn)這個(gè)功能。而且,事實(shí)證明,確實(shí)滿足了我的需求。
項(xiàng)目開(kāi)發(fā)中往往需要在接口返回較慢的時(shí)候給出loading狀態(tài),防止在接口返回值的過(guò)程中用戶多次點(diǎn)擊,之前我們?cè)?code>Vue中也添加過(guò)這個(gè)功能,而且還在其中遇到過(guò)由于單例模式出現(xiàn)的問(wèn)題,以及如何解決這個(gè)問(wèn)題大家可以自行百度。
Vue中全局loading組件直接通過(guò)創(chuàng)建實(shí)例的方式,在ant.d中我們的Spin組件需要包裹想要遮罩的元素,我們?nèi)绾卧谡?qǐng)求接口的入口統(tǒng)一添加全局loading呢?
這里我們就用到了redux(react+redux超簡(jiǎn)單入門實(shí)例這里可以帶你迅速了解并使用redux)
使用redux實(shí)現(xiàn)全局Loading
添加Spin
首先在main文件中添加全局Spin,main.js文件包裹了所有菜單欄所包含的內(nèi)容(除了登錄頁(yè),基本都包含在這里)
// 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
接口攔截設(shè)置Loading顯示
我們需要在接口發(fā)出請(qǐng)求的時(shí)候設(shè)置Loading顯示,在返回以后設(shè)置其為隱藏。
actions
定義兩個(gè)action動(dòng)作,一個(gè)用來(lái)打開(kāi)loading一個(gè)用來(lái)關(guān)閉loading
// actions/index.js export const OPENPAGELOADING = 'OpenPageLoading' export const CLOSEPAGELOADING = 'ClosePageLoading'
reducers
通過(guò)不同事件來(lái)觸發(fā)值的改變
// 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
接口入口文件中設(shè)置全局的值
import axios from 'axios'
import { message } from 'antd'
import store from '../store'
import { OPENPAGELOADING, CLOSEPAGELOADING } from '../actions'
let instance = axios.create({
baseURL: '',
timeout: 5000
})
/* 請(qǐng)求攔截 */
instance.interceptors.request.use(config => {
store.dispatch({type: OPENPAGELOADING})
return config
}, error => {
store.dispatch({type: CLOSEPAGELOADING })
message.error('請(qǐng)求超時(shí)')
return Promise.reject(error)
})
/* 響應(yīng)攔截 */
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 || '獲取接口數(shù)據(jù)錯(cuò)誤')
return Promise.reject()
}
}, error => {
store.dispatch({type: CLOSEPAGELOADING })
message.error('服務(wù)錯(cuò)誤')
return Promise.reject(error)
})
export default instance
在發(fā)出接口請(qǐng)求的時(shí)候通過(guò)store.dispatch({type: 'OpenPageLoading'})派發(fā)操作
需要注意的時(shí)候,在無(wú)論接口返回是什么的情況下需要通過(guò)store.dispatch({type: 'ClosePageLoading'})來(lái)關(guān)閉loading。
監(jiān)測(cè)store中l(wèi)oading值設(shè)置是否顯示Spin
到此為止我們store中的全局loading的值已經(jīng)發(fā)生了滿足需求的改變,
下面要做的就是在每次值發(fā)生改變的時(shí)候我們能在使用Spin的地方監(jiān)聽(tīng)到,
說(shuō)到監(jiān)聽(tīng)是不是就想到了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 () {
// 重點(diǎn)
// 重點(diǎn)
// 重點(diǎn)
// 監(jiān)聽(tīng)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
是的,通過(guò)監(jiān)聽(tīng)來(lái)設(shè)置當(dāng)前組件的state的值。
優(yōu)化
如果你一個(gè)頁(yè)面只有一個(gè)接口,那么你完成上述就搞定了。
但是,如果你一個(gè)頁(yè)面有多個(gè)接口,當(dāng)其中有個(gè)接口返回值很慢的時(shí)候你就會(huì)發(fā)現(xiàn)問(wèn)題,就是當(dāng)一個(gè)接口pending結(jié)束全局loading就消失了,這個(gè)當(dāng)然不是我們想要的結(jié)果,理想的亞子是當(dāng)前頁(yè)面所有接口都pending結(jié)束后才消失loading。
這個(gè)問(wèn)題之前在做elementUI+Vue做全局loading時(shí)候也遇到過(guò),其實(shí)思路是一樣的,思路可參考elementUI全局Loading單例模式。
因?yàn)閯?chuàng)建全局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
})
/* 添加一個(gè)計(jì)數(shù)器 */
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})
}
}
/* 請(qǐng)求攔截 */
instance.interceptors.request.use(config => {
showFullScreenLoading()
return config
}, error => {
tryHideFullScreenLoading()
message.error('請(qǐng)求超時(shí)')
return Promise.reject(error)
})
/* 響應(yīng)攔截 */
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 || '獲取接口數(shù)據(jù)錯(cuò)誤')
return Promise.reject()
}
}, error => {
tryHideFullScreenLoading()
message.error('服務(wù)錯(cuò)誤')
return Promise.reject(error)
})
export default instance
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
React Native 啟動(dòng)流程詳細(xì)解析
這篇文章主要介紹了React Native 啟動(dòng)流程簡(jiǎn)析,文以 react-native-cli 創(chuàng)建的示例工程(安卓部分)為例,給大家分析 React Native 的啟動(dòng)流程,需要的朋友可以參考下2021-08-08
React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例
這篇文章主要為大家介紹了React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
React使用UI(Ant?Design)框架的詳細(xì)過(guò)程
Ant?Design主要用于中后臺(tái)系統(tǒng)的使用,它提供了豐富的組件和工具,可以幫助開(kāi)發(fā)人員快速構(gòu)建出美觀、易用的界面,同時(shí),Ant?Design還提供了詳細(xì)的文檔和示例,方便開(kāi)發(fā)者學(xué)習(xí)和使用,這篇文章主要介紹了React使用UI(Ant?Design)框架,需要的朋友可以參考下2023-12-12
React Fiber樹(shù)的構(gòu)建和替換過(guò)程講解
React Fiber樹(shù)的創(chuàng)建和替換過(guò)程運(yùn)用了雙緩存技術(shù),直接將舊的 fiber 樹(shù)替換成新的 fiber 樹(shù),這樣做的好處是省去了直接在頁(yè)面上渲染時(shí)的計(jì)算時(shí)間,避免計(jì)算量大導(dǎo)致的白屏、卡頓,現(xiàn)在你一定還不太理解,下面進(jìn)行詳細(xì)講解,需要的朋友可以參考下2022-12-12
Remix?路由模塊輸出對(duì)象handle函數(shù)
這篇文章主要為大家介紹了Remix?路由模塊輸出對(duì)象handle函數(shù)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
react使用antd-design中select不能及時(shí)刷新問(wèn)題及解決
這篇文章主要介紹了react使用antd-design中select不能及時(shí)刷新問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
concent漸進(jìn)式重構(gòu)react應(yīng)用使用詳解
這篇文章主要為大家介紹了concent漸進(jìn)式重構(gòu)react應(yīng)用的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

