【问题标题】:React Hooks: Display global spinner using axios interceptor?React Hooks:使用 axios 拦截器显示全局微调器?
【发布时间】:2020-04-07 16:43:39
【问题描述】:

我想添加一个加载器组件,以便在 React 中进行 API 调用时呈现。我想使用 react context + hooks 而不是 redux

正如 react hooks 的规则所说,我们不应该在 react 组件之外使用 react hooks。但是我需要在 Axios 拦截器中发送SHOW_LOADERHIDE_LOADER,如下所示。

有没有办法做到这一点?

import axios from "axios";
axios.interceptors.request.use(
  config => {
    dispatch({
    type: "SHOW_LOADER"
})
    return config;
  },
  error => {
     dispatch({
    type: "HIDE_LOADER"
})
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  response => {
    dispatch({
    type: "HIDE_LOADER"
})
    return response;
  },
  error => {
    dispatch({
    type: "HIDE_LOADER"
})
    return Promise.reject(error);
  }
);
function GlobalLoader(){
    const [state,dispatch] = useContext(LoaderContext);
    return(
        <div>
            {
                state.loadStatus &&
                    <Loader
                    type = "Puff"
                    color = "#00BFFF"
                    height = {100}
                    width = {100}
                    timeout = {3000} />
            }
        </div>
    );
}

export default GlobalLoader;

如果需要更多信息,请告诉我。:)

【问题讨论】:

  • 为什么不将拦截器移动到GlobalLoader 中的useEffect 内?
  • useEffect 在子组件中发起 API 请求时不会被执行。
  • useEffect 不需要执行,您正在将一个函数传递给拦截器,它将执行
  • @Agney axios.interceptor 未检测到子组件中发出的 API 请求。

标签: reactjs axios react-hooks interceptor react-context


【解决方案1】:

使用axios.create(config) 创建一个 axios 实例。在useEffect() 中使用这个实例来添加可以影响状态的拦截器(reducer 在这里是多余的)。现在到处使用实例,拦截器会引起状态的变化。

注意:由于多个请求可以开始/和或结束,您应该使用计数器。请求增加,响应减少。如果计数器不是0,则说明正在加载应用程序。

const { useState, useMemo, useEffect } = React;

const ax = axios.create(); // export this and use it in all your components

const useAxiosLoader = () => {
  const [counter, setCounter] = useState(0);
    
  const interceptors = useMemo(() => {
    const inc = () => setCounter(counter => counter + 1);
    const dec = () => setCounter(counter => counter - 1);
    
    return ({
      request: config => (inc(), config),
      response: response => (dec(), response),
      error: error => (dec(), Promise.reject(error)),
    });
  }, []); // create the interceptors
  
  useEffect(() => {
    // add request interceptors
    const reqInterceptor = ax.interceptors.request.use(interceptors.request, interceptors.error);
    // add response interceptors
    const resInterceptor = ax.interceptors.response.use(interceptors.response, interceptors.error);
    return () => {
      // remove all intercepts when done
      ax.interceptors.request.eject(reqInterceptor);
      ax.interceptors.response.eject(resInterceptor);
    };
  }, [interceptors]);
  
  return [counter > 0];
};

const GlobalLoader = () => {
    const [loading] = useAxiosLoader();
    
    return(
      <div>
      {
        loading ? 'loading' : 'not loading'
      }
      </div>
    );
}

const callApi = (err) => ax.get(err ? 'https://asdf' : 'https://www.boredapi.com/api/activity')

// make a request by using the axios instance
setTimeout(() => {
  callApi();
  callApi(true);
  callApi();
}, 1000);

ReactDOM.render(
  <GlobalLoader />,
  root
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

【讨论】:

  • 谢谢@Ori Drori,但我无法对其进行测试,因为当我将其放入应用程序时,该代码会进入无限循环。您能否提供更新的版本。以便我可以接受这个作为答案。
  • console.log() 添加到counter(在钩子内),并查看是否从0 变为请求数,然后返回0。如果不是,你有一些在后台被多次调用的请求(例如轮询),并且计数器永远不会重置。
  • setTimeout 块不是代码的一部分。在其他组件中进行调用,而不是加载程序。全局加载器不应该发出请求,只是显示和隐藏加载器。在 GlobalLoader 下创建另一个组件,即 not,并从该组件发出请求。对于这个例子,我刚刚做了几次调用作为演示,我什至在 React 之外进行了调用。
  • 好像是组件卸载时调用了拦截器。它不应该与请求的类型有关。
  • 非常感谢,您的指导启发了我,我解决了问题:)
猜你喜欢
  • 2018-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 2020-03-04
  • 1970-01-01
  • 2019-06-26
相关资源
最近更新 更多