Javascript React loader won';t在'期间保持可见;待定';axios请求
我使用react上下文和axios拦截器隐藏/显示加载微调器。虽然它在触发请求和响应时正确隐藏和显示,但我的加载微调器仅在网络请求开始时显示。请求激发,然后进入“挂起”状态。在“挂起”状态期间,将启动响应拦截器并隐藏加载微调器 如何确保加载微调器在挂起的请求期间保持可见 我尝试在返回请求、响应和错误(包括计数)时添加一些控制台日志以激发,结果显示(对于两个请求)成功地从0-1-2-1-0开始,但在chrome devtools的网络选项卡中,即使返回了所有请求,也显示为挂起 编辑:我以为经过一些重构后它还能工作,但这是不可能的。添加了更新的代码Javascript React loader won';t在'期间保持可见;待定';axios请求,javascript,reactjs,axios,Javascript,Reactjs,Axios,我使用react上下文和axios拦截器隐藏/显示加载微调器。虽然它在触发请求和响应时正确隐藏和显示,但我的加载微调器仅在网络请求开始时显示。请求激发,然后进入“挂起”状态。在“挂起”状态期间,将启动响应拦截器并隐藏加载微调器 如何确保加载微调器在挂起的请求期间保持可见 我尝试在返回请求、响应和错误(包括计数)时添加一些控制台日志以激发,结果显示(对于两个请求)成功地从0-1-2-1-0开始,但在chrome devtools的网络选项卡中,即使返回了所有请求,也显示为挂起 编辑:我以为经过一些
import React,{useReducer,useRef,useffect,useCallback}来自“React”;
从“api/api”导入{api};
从“减速器/加载减速器”导入加载减速器;
const LoadingContext=React.createContext();
export const LoadingProvider=({children})=>{
const[loader,dispatch]=useReducer(LoadingReducer{
加载:false,
计数:0,
});
const loaderKeepAlive=useRef(null),
showLoader=useRef(null);
const showLoading=useCallback(()=>{
派遣({
类型:“显示加载”,
});
},[发送];
const hideLoading=useCallback(()=>{
loaderKeepAlive.current=设置超时(()=>{
派遣({
类型:“隐藏加载”,
});
}, 3000);
返回clearTimeout(loaderKeepAlive.current);
},[发送];
const requestHandler=useCallback(
(请求)=>{
分派({type:“SET_COUNT”,COUNT:1});
返回Promise.resolve({…request});
},
[快讯]
);
const errorHandler=useCallback(
(错误)=>{
分派({type:“SET_COUNT”,COUNT:-1});
返回承诺。拒绝({…错误});
},
[快讯]
);
const successHandler=useCallback(
(回应)=>{
分派({type:“SET_COUNT”,COUNT:-1});
返回Promise.resolve({…response});
},
[快讯]
);
useffect(()=>{
if(loader.count==0){
hideLoading();
clearTimeout(showLoader.current);
}否则{
showLoader.current=设置超时(()=>{
showLoading();
}, 1000);
}
},[showLoader,showLoading,hideLoading,loader.count]);
useffect(()=>{
if(!api.interceptors.request.handlers[0]){
api.interceptors.request.use(
(请求)=>requestHandler(请求),
(错误)=>errorHandler(错误)
);
}
if(!api.interceptors.response.handlers[0]){
api.interceptors.response.use(
(响应)=>successHandler(响应),
(错误)=>errorHandler(错误)
);
}
return()=>{
clearTimeout(showLoader.current);
};
},[errorHandler、requestHandler、successHandler、showLoader]);
返回(
{儿童}
);
};
导出默认加载上下文;
我认为更标准的方法是只利用加载
状态有条件地呈现微调器以及承诺从DOM中移除微调器的结果(参见下面的演示)。通常,拦截器用于从API响应返回错误,因为axios默认为状态错误(例如,404-notfound
)
例如,用于显示API错误的自定义axios拦截器:
import get from "lodash.get";
import axios from "axios";
const { baseURL } = process.env;
export const app = axios.create({
baseURL
});
app.interceptors.response.use(
response => response,
error => {
const err = get(error, ["response", "data", "err"]);
return Promise.reject(err || error.message);
}
);
export default app;
演示
代码
App.js
import React, { useEffect, useCallback, useState } from "react";
import fakeApi from "./api";
import { useAppContext } from "./AppContext";
import Spinner from "./Spinner";
const App = () => {
const { isLoading, error, dispatch } = useAppContext();
const [data, setData] = useState({});
const fetchData = useCallback(async () => {
try {
// this example uses a fake api
// if you want to trigger an error, then pass a status code other than 200
const res = await fakeApi.get(200);
setData(res.data);
dispatch({ type: "loaded" });
} catch (error) {
dispatch({ type: "error", payload: error.toString() });
}
}, [dispatch]);
const reloadData = useCallback(() => {
dispatch({ type: "reset" });
fetchData();
}, [dispatch, fetchData]);
useEffect(() => {
fetchData();
// optionally reset context state on unmount
return () => {
dispatch({ type: "reset" });
};
}, [dispatch, fetchData]);
if (isLoading) return <Spinner />;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<div style={{ textAlign: "center" }}>
<pre
style={{
background: "#ebebeb",
margin: "0 auto 20px",
textAlign: "left",
width: 600
}}
>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
<button type="button" onClick={reloadData}>
Reload
</button>
</div>
);
};
export default App;
import React, { createContext, useContext, useReducer } from "react";
const AppContext = createContext();
const initialReducerState = {
isLoading: true,
error: ""
};
const handleLoading = (state, { type, payload }) => {
switch (type) {
case "loaded":
return { isLoading: false, error: "" };
case "error":
return { isLoading: false, error: payload };
case "reset":
return initialReducerState;
default:
return state;
}
};
export const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(handleLoading, initialReducerState);
return (
<AppContext.Provider
value={{
...state,
dispatch
}}
>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default AppContextProvider;
import React from "react";
const Spinner = () => <div className="loader">Loading...</div>;
export default Spinner;
重新加载
);
};
导出默认应用程序;
AppContext.js
import React, { useEffect, useCallback, useState } from "react";
import fakeApi from "./api";
import { useAppContext } from "./AppContext";
import Spinner from "./Spinner";
const App = () => {
const { isLoading, error, dispatch } = useAppContext();
const [data, setData] = useState({});
const fetchData = useCallback(async () => {
try {
// this example uses a fake api
// if you want to trigger an error, then pass a status code other than 200
const res = await fakeApi.get(200);
setData(res.data);
dispatch({ type: "loaded" });
} catch (error) {
dispatch({ type: "error", payload: error.toString() });
}
}, [dispatch]);
const reloadData = useCallback(() => {
dispatch({ type: "reset" });
fetchData();
}, [dispatch, fetchData]);
useEffect(() => {
fetchData();
// optionally reset context state on unmount
return () => {
dispatch({ type: "reset" });
};
}, [dispatch, fetchData]);
if (isLoading) return <Spinner />;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<div style={{ textAlign: "center" }}>
<pre
style={{
background: "#ebebeb",
margin: "0 auto 20px",
textAlign: "left",
width: 600
}}
>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
<button type="button" onClick={reloadData}>
Reload
</button>
</div>
);
};
export default App;
import React, { createContext, useContext, useReducer } from "react";
const AppContext = createContext();
const initialReducerState = {
isLoading: true,
error: ""
};
const handleLoading = (state, { type, payload }) => {
switch (type) {
case "loaded":
return { isLoading: false, error: "" };
case "error":
return { isLoading: false, error: payload };
case "reset":
return initialReducerState;
default:
return state;
}
};
export const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(handleLoading, initialReducerState);
return (
<AppContext.Provider
value={{
...state,
dispatch
}}
>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default AppContextProvider;
import React from "react";
const Spinner = () => <div className="loader">Loading...</div>;
export default Spinner;
index.js
import React, { useEffect, useCallback, useState } from "react";
import fakeApi from "./api";
import { useAppContext } from "./AppContext";
import Spinner from "./Spinner";
const App = () => {
const { isLoading, error, dispatch } = useAppContext();
const [data, setData] = useState({});
const fetchData = useCallback(async () => {
try {
// this example uses a fake api
// if you want to trigger an error, then pass a status code other than 200
const res = await fakeApi.get(200);
setData(res.data);
dispatch({ type: "loaded" });
} catch (error) {
dispatch({ type: "error", payload: error.toString() });
}
}, [dispatch]);
const reloadData = useCallback(() => {
dispatch({ type: "reset" });
fetchData();
}, [dispatch, fetchData]);
useEffect(() => {
fetchData();
// optionally reset context state on unmount
return () => {
dispatch({ type: "reset" });
};
}, [dispatch, fetchData]);
if (isLoading) return <Spinner />;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<div style={{ textAlign: "center" }}>
<pre
style={{
background: "#ebebeb",
margin: "0 auto 20px",
textAlign: "left",
width: 600
}}
>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
<button type="button" onClick={reloadData}>
Reload
</button>
</div>
);
};
export default App;
import React, { createContext, useContext, useReducer } from "react";
const AppContext = createContext();
const initialReducerState = {
isLoading: true,
error: ""
};
const handleLoading = (state, { type, payload }) => {
switch (type) {
case "loaded":
return { isLoading: false, error: "" };
case "error":
return { isLoading: false, error: payload };
case "reset":
return initialReducerState;
default:
return state;
}
};
export const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(handleLoading, initialReducerState);
return (
<AppContext.Provider
value={{
...state,
dispatch
}}
>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default AppContextProvider;
import React from "react";
const Spinner = () => <div className="loader">Loading...</div>;
export default Spinner;
从“React”导入React;
从“react dom”导入react dom;
从“/AppContext”导入AppContextProvider;
从“/App”导入应用程序;
导入“/styles.css”;
const rootElement=document.getElementById(“根”);
ReactDOM.render(
,
根元素
);
Ah打字错误,谢谢!我不认为这会成为问题,因为加载程序保持活动状态只是为了确保它不会闪烁,而是让它保持可见一段时间。因此,我也有一个特定的appcontext。但我有大约20多个api调用(截至目前,还会有更多调用),我不想在整个应用程序中重复这么多代码,我想通过使用拦截器保持它的干燥。否则,我将需要多次分派,以及分派以将数据保存到帐户上下文。有什么想法吗?如果是这样的话,我建议使用可重用的高阶组件包装器(或自定义的可重用加载挂钩)而不是上下文。建议使用上下文在应用程序树中不同分支中的组件之间共享数据(例如,亮/暗主题)。相反,HOC包装器将提供可由父组件或子组件操纵的可重用状态/方法。由于它是独立的,所以可以与其他需要它的组件并行安装。如果没有,请提供一个工作示例,以便我能够理解您试图实现的目标。您可以从上面我的代码沙盒示例开始(不需要花哨,只需简单的代码就足够了)。