Javascript React loader won';t在'期间保持可见;待定';axios请求

Javascript React loader won';t在'期间保持可见;待定';axios请求,javascript,reactjs,axios,Javascript,Reactjs,Axios,我使用react上下文和axios拦截器隐藏/显示加载微调器。虽然它在触发请求和响应时正确隐藏和显示,但我的加载微调器仅在网络请求开始时显示。请求激发,然后进入“挂起”状态。在“挂起”状态期间,将启动响应拦截器并隐藏加载微调器 如何确保加载微调器在挂起的请求期间保持可见 我尝试在返回请求、响应和错误(包括计数)时添加一些控制台日志以激发,结果显示(对于两个请求)成功地从0-1-2-1-0开始,但在chrome devtools的网络选项卡中,即使返回了所有请求,也显示为挂起 编辑:我以为经过一些

我使用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包装器将提供可由父组件或子组件操纵的可重用状态/方法。由于它是独立的,所以可以与其他需要它的组件并行安装。如果没有,请提供一个工作示例,以便我能够理解您试图实现的目标。您可以从上面我的代码沙盒示例开始(不需要花哨,只需简单的代码就足够了)。