V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Danswerme
V2EX  ›  React

[React]麻烦大家帮我看看代码,为什么 axios 的拦截器没能正常捕获到错误?

  •  
  •   Danswerme · 2019-12-27 22:39:34 +08:00 · 3211 次点击
    这是一个创建于 1552 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码在这里 https://codesandbox.io/s/vigilant-napier-wzd4e

    为什么拦截器里未能正常捕获 HomePage 组件里的 401 错误呢?将函数放入 setTimeout 里执行时又可以捕获到错误,麻烦大家帮我看看问题出在哪里。

    14 条回复    2019-12-28 10:52:36 +08:00
    manami
        1
    manami  
       2019-12-28 09:07:17 +08:00
    手机刷不出来代码。会不会是 axios 是异步请求导致的。“将函数放入 setTimeout 里执行时又可以捕获到错误”,setTimeout 严格意义上不是异步。
    Danswerme
        2
    Danswerme  
    OP
       2019-12-28 09:22:51 +08:00
    import React, { useContext, useEffect } from "react";
    import axios from "axios";

    export const AjaxContext = React.createContext();
    export const useAjax = () => useContext(AjaxContext);

    const instance = axios.create({
    timeout: 5000
    });

    export function AjaxProvider({ children }) {
    useEffect(() => {
    const onReq = config => {
    config.headers["Authorization"] = localStorage.getItem("token");
    return config;
    };
    const onErr = err => Promise.reject(err);

    const flag = instance.interceptors.request.use(onReq, onErr);
    return () => instance.interceptors.request.eject(flag);
    });

    useEffect(() => {
    const onRes = res => res;
    const onErr = err => {
    if (err.response) {
    const { status } = err.response;
    if (status === 401) {
    console.log("捕获到了!");
    }
    }

    return Promise.reject(err);
    };

    const flag = instance.interceptors.response.use(onRes, onErr);
    return () => instance.interceptors.response.eject(flag);
    });

    const ajax = {
    get: path => instance.get(path),
    post: (path, data) => instance.post(path, data),
    put: (path, data) => instance.put(path, data),
    delete: path => instance.delete(path)
    };

    return <AjaxContext.Provider value={ajax} children={children} />;
    }
    Danswerme
        3
    Danswerme  
    OP
       2019-12-28 09:23:13 +08:00
    import React, { useEffect, Component } from "react";
    import { useAjax, AjaxContext } from "./Ajax";

    export function HomePage() {
    const ajax = useAjax();

    async function testToken() {
    try {
    const result = await ajax.get(
    "https://api.youneedabudget.com/v1/budgets"
    );
    } catch (err) {
    console.log(err);
    }
    }

    useEffect(() => {
    // testToken();

    setTimeout(testToken);
    });

    return (
    <div>
    App
    <button onClick={testToken}>GoGoGo</button>
    </div>
    );
    }
    Danswerme
        4
    Danswerme  
    OP
       2019-12-28 09:23:31 +08:00
    import React from "react";
    import ReactDOM from "react-dom";
    import { AjaxProvider } from "./Ajax";
    import { HomePage } from "./HomePage";

    function App() {
    return (
    <AjaxProvider>
    <HomePage />
    </AjaxProvider>
    );
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    Danswerme
        5
    Danswerme  
    OP
       2019-12-28 09:26:31 +08:00
    @manami 代码我贴在楼上啦,你看看。 如果用按钮点击执行这个 ajax 函数时,axios 拦截器可以正常捕获到错误,而在组件刚挂载完时执行这个 ajax 函数就无法捕获到,不知道是哪里的问题。
    manami
        6
    manami  
       2019-12-28 09:43:06 +08:00
    @Danswerme 看完代码懵逼。axios 还有拦截器这种用法,期待其他专业前端的回答,学习学习
    ztmqg
        7
    ztmqg  
       2019-12-28 09:57:27 +08:00 via iPhone   ❤️ 1
    @manami 是的,基本上都会封装一下,后续省事
    Danswerme
        8
    Danswerme  
    OP
       2019-12-28 09:58:16 +08:00
    @manami 我也是菜鸟,以前都是直接将 axios 封装成一个 http 模块,然后在用到的地方引入。这次在 react-china http://react-china.org/t/topic/32941 看到了将 axios 直接放在 context 里的方法,特地用的试了下,结果就卡在这里了
    miniwade514
        9
    miniwade514  
       2019-12-28 09:58:32 +08:00 via iPhone
    在手机上艰难地看了很久,越看越懵逼。发请求这种事情为什么也要用 hooks,跟 react 组件的生命周期挂上钩,排查问题都不方便啊。
    你说的能捕获,是指执行了 console.log("捕获到了") 那一行?
    simonv3ex
        10
    simonv3ex  
       2019-12-28 10:00:47 +08:00
    贼 jer 卡
    const result = await ajax.get(
    "https://api.youneedabudget.com/v1/budgets"
    ).catch(err=>{
    alert(err);
    });
    Danswerme
        11
    Danswerme  
    OP
       2019-12-28 10:13:55 +08:00
    @miniwade514 是的,我指的捕获就是那一行。我是将用户 token 保存在 localStorage 里,用户信息、是否登录等状态放在 redux 里,每次第一次打开页面时会发请求去验证 token 是否合法,如果合法就将服务器返回的用户信息放进 redux 里,并将是否登录设置为 true。

    不合法时服务器返回 401 就发起一个 dispatch,将是否登录设置为 false,下面的 PrivateRoute 会直接跳转到 login 页面。
    Danswerme
        12
    Danswerme  
    OP
       2019-12-28 10:15:26 +08:00
    @simonv3ex 呃,你指的什么卡?
    miniwade514
        13
    miniwade514  
       2019-12-28 10:39:16 +08:00 via iPhone
    没记错的话,AjaxProvider 里的 useEffect 是在 Homepage 的 useEffect 之后执行的,所以你第一次发请求的时候,拦截器还没注册上
    Danswerme
        14
    Danswerme  
    OP
       2019-12-28 10:52:36 +08:00
    @miniwade514 谢谢指点!是这里的问题了,我研究研究应该将这个验证逻辑放到哪里去
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5442 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 08:39 · PVG 16:39 · LAX 01:39 · JFK 04:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.