普通版
layout.jsx
import { createContext, useReducer, useLayoutEffect } from "react";
import AxiosDemo from "./axiosDemo";
import { axiosConfig } from "./servers/index";
import "./styles.css";
const initState = { loading: false };
export const GlobalContext = createContext();
const globalReducer = (state, action) => {
switch (action.type) {
case "updateState":
return {
...state,
...action.data
};
default:
throw new Error();
}
};
export default function App() {
const [globalState, dispatch] = useReducer(globalReducer, initState);
const { loading } = globalState;
useLayoutEffect(() => {
axiosConfig({ loading, dispatch });
}, []);
return (
<GlobalContext.Provider value={{ loading, dispatch }}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<AxiosDemo />
{/* global loading */}
<div
style={{
display: loading ? "block" : "none",
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
background: " rgba(255, 255, 255, 0.8)"
}}
>
<div
style={{
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
loading...
</div>
</div>
</div>
</GlobalContext.Provider>
);
}
axiosDemo.jsx
import { useEffect, useContext } from "react";
import axios from "axios";
import { GlobalContext } from "./App";
const getInfo = () => {
return axios.get("https://api.github.com/users/ruanyf");
};
const AxiosDemo = () => {
const { loading } = useContext(GlobalContext);
useEffect(() => {
getInfo().then((res) => {
// console.log(res, "res--0");
getInfo().then((res1) => {
// console.log(res1, "res--1");
});
});
getInfo();
}, []);
return (
<div>
<div>axios demo</div>
<button
onClick={() => {
getInfo().then((res) => {
// console.log(res, "res--click");
});
}}
>
click getInfo(loadingStatus:{`${loading}`})
</button>
</div>
);
};
export default AxiosDemo;
axiosConfig.js
import axios from "axios";
// 是否展示 loading (做一个类似任务队列,避免 loading 闪动)
const requestUrls = [];
const isShowLoading = ({ requestUrl, responseUrl, loading, dispatch }) => {
if (requestUrl) {
requestUrls.push(requestUrl);
} else {
const index = requestUrls.indexOf(responseUrl as string);
if (index !== -1) {
requestUrls.splice(index, 1);
}
}
if (requestUrls.length > 0) {
// ⚠️⚠️⚠️ 由于传入的 loading 值(优化点,多个接口一起请求就会一直dispatch)在 useLayoutEffect 没有监听 loading 值,故在此只能一直拿到 false;可以参考在 context 中改变 loading 值时加入到 window 里面,dispatch 同理,即不需要传入 {loading, dispatch },从全局对象 window 拿这两个值
if (loading === false) {
dispatch({ type: "updateState", data: { loading: true } });
}
} else {
dispatch({
type: "updateState",
data: { loading: false }
});
}
};
export const axiosConfig = ({ loading, dispatch }) => {
axios.interceptors.request.use(
function (config) {
// console.log(1111111);
const { url } = config;
isShowLoading({ requestUrl: url, loading, dispatch });
return config;
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
// console.log(222222);
const {
config: { url }
} = response;
isShowLoading({ responseUrl: url, loading, dispatch });
return response;
},
function (error) {
// isShowLoading({ responseUrl: url, loading, dispatch }); 失败也需要调 isShowLoading,该处没拿到 url,故注释了,真实场景需要开启
return Promise.reject(error);
}
);
};
window 版
layout.jsx
import { createContext, useReducer } from "react";
import AxiosDemo from "./axiosDemo";
import { axiosConfig } from "./servers/index";
import "./styles.css";
axiosConfig();
const initState = { loading: false };
export const GlobalContext = createContext();
const globalReducer = (state, action) => {
switch (action.type) {
case "updateState":
return {
...state,
...action.data
};
default:
throw new Error();
}
};
export default function App() {
const [globalState, dispatch] = useReducer(globalReducer, initState);
const { loading } = globalState;
window.customWindowState = {};
window.customWindowState.loading = loading;
window.customWindowState.dispatch = dispatch;
return (
<GlobalContext.Provider value={{ loading, dispatch }}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<AxiosDemo />
{/* loading */}
<div
style={{
display: loading ? "block" : "none",
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
background: " rgba(255, 255, 255, 0.8)"
}}
>
<div
style={{
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
loading...
</div>
</div>
</div>
</GlobalContext.Provider>
);
}
axiosConfig.js
import axios from "axios";
// 是否展示 loading (做一个类似任务队列,避免 loading 闪动)
const requestUrls = [];
const isShowLoading = ({ requestUrl, responseUrl }) => {
const {
customWindowState: { loading, dispatch }
} = window;
console.log(loading, "loading");
if (requestUrl) {
requestUrls.push(requestUrl);
} else {
const index = requestUrls.indexOf(responseUrl as string);
if (index !== -1) {
requestUrls.splice(index, 1);
}
}
if (requestUrls.length > 0) {
if (loading === false) {
dispatch({ type: "updateState", data: { loading: true } });
}
} else {
dispatch({
type: "updateState",
data: { loading: false }
});
}
};
export const axiosConfig = () => {
axios.interceptors.request.use(
function (config) {
// console.log(1111111);
const { url } = config;
isShowLoading({ requestUrl: url });
return config;
},
function (error) {
return Promise.reject(error);
}
);
// Add a response interceptor
axios.interceptors.response.use(
function (response) {
// console.log(222222);
const {
config: { url }
} = response;
isShowLoading({ responseUrl: url });
return response;
},
function (error) {
// isShowLoading({ responseUrl: url }); // isShowLoading({ responseUrl: url, loading, dispatch }); 失败也需要调 isShowLoading,该处没拿到 url,故注释了,真实场景需要开启
return Promise.reject(error);
}
);
};