React项目中实现axios路由跳转取消上个路由页面的所有请求 和 取消当前路由页面的重复请求

一、 抛出问题

1、当一个页面的请求过长时,点击了跳转路由到新的页面,但是上一个路由的请求结果在当前路由页面提示,非常难看且容易让客户混淆。(所以需要路由跳转取消上个路由页面的所有请求)
2、在当前页面某个请求长时间没拿到返回结果,然后用户又点击发起了相同请求,这时有可能后发送的请求返回的数据被先发送的请求覆盖,这样就得不到最新请求数据了。(所以需要取消当前路由页面的重复请求,以保证后发送的请求返回的数据不会被先发送的请求覆盖)
3、上面两种情况都会浪费网络资源

二、解决问题

1、我们在axios封装文件axios.js中写入如下代码:
import axios from 'axios';
import { message } from 'antd';
import qs from 'qs';
// 引入自己项目中的配置文件
import { hrefLogin } from 'src/configs';

// 响应时间
axios.defaults.timeout = 300000; // 5 min
axios.defaults.headers.post['Content-Type'] = 'application/json';

// 取消请求操作
const allPendingRequestsRecord = [];
const pending = {};
const removeAllPendingRequestsRecord = () => {
    allPendingRequestsRecord && allPendingRequestsRecord.forEach((func) => {
        // 取消请求(调用函数就是取消该请求)
        func('路由跳转了取消所有请求');
    });
    // 移除所有记录
    allPendingRequestsRecord.splice(0);
};

// 取消同一个重复的ajax请求
const removePending = (key, isRequest = false) => {
    if (pending[key] && isRequest) {
        pending[key]('取消重复请求');
    }
    delete pending[key];
};

// 取消所有请求的函数
export const getConfirmation = (mes = '', callback = () => {}) => {
    removeAllPendingRequestsRecord();
    callback();
};

// request interceptor
axios.interceptors.request.use(
    (config) => {
        // 在请求发送前执行一下取消操作,防止连续点击重复发送请求(例如连续点击2次按钮)
        let reqData = '';
        // 处理如url相同请求参数不同时上一个请求被屏蔽的情况
        if (config.method === 'get') {
            reqData = config.url + config.method + JSON.stringify(config.params);
        } else {
            reqData = config.url + config.method + JSON.stringify(config.data);
        }
        // 如果用户连续点击某个按钮会发起多个相同的请求,可以在这里进行拦截请求并取消上一个重复的请求
        removePending(reqData, true);
        // 设置请求的 cancelToken(设置后就能中途控制取消了)
        config.cancelToken = new axios.CancelToken((c) => {
            pending[reqData] = c;
            allPendingRequestsRecord.push(c);
        });

        // application/x-www-form-urlencoded
        if (config.resType === 'form') {
            config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            config.data = qs.stringify(config.data);
            return config;
        }
        return config;
    },
    (error) => Promise.reject(error),
);

// response interceptor
axios.interceptors.response.use(
    (response) => {
        if (response.status >= 400) {
            // 没登录/过期/异常
            if (response.status === 401) {
                message.error('登录态过期,请重新登录');
                // ...写入自己的代码逻辑
                return;
            }
            message.error('网络连接异常');
            return Promise.reject(response);
        }

        return response;
    },
    (error) => {
        // 终结由于取消重复请求而引发的报错提示
        if (axios.isCancel(error)) {
            return new Promise(() => {});
        }
        if (error.response) {
            if (error.response.status === 401) {
                message.error('登录态过期,请重新登录');
                // ...写入自己的代码逻辑
            } else if (error.response.status === 403) {
                message.error('您尚无权限访问');
                // ...写入自己的代码逻辑
            } else if (error.response.status === 511) {
                message.error('您尚未登录,请登录');
                // ...写入自己的代码逻辑
            } else {
                message.error('网络连接异常');
            }
        }
        return Promise.reject(error);
    },
);

export default axios;

2、在React路由跳转时取消上个页面的所有请求

这里是自己封装的react路由文件router.js。

import React, { Suspense } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
// 引入上面封装的axios文件中的getConfirmation函数
import { getConfirmation } from 'src/tools/axios';

import { Loading } from 'src/components';

const SubRoutes = (route) => (route.tag === 'Redirect' ? (
    <Redirect
        from={route.from}
        to={route.to}
        exact={route.exact}
        render={(props) => (
            <route.component {...props} routes={route.children} />
        )}
    />
) : (
    <Route
        path={route.path}
        exact={route.exact}
        render={(props) => {
            // 路由跳转取消上个组件所有的pending请求
            getConfirmation();
            return (
                <route.component {...props} routes={route.children} />
            );
        }}
    />
));

const Routes = (props) => (
    <Suspense fallback={<Loading />}>
        <Switch>
            {props.routes.map((route) => (
                <SubRoutes key={JSON.stringify(route)} {...route} />
            ))}
        </Switch>
    </Suspense>
);

export { Routes };

三、App.js中使用路由

import React, { useEffect } from 'react';
import { HashRouter as Router } from 'react-router-dom';
import { message } from 'antd';

// 引入上面封装的路由文件
import { Routes } from 'src/router';
// 路由组件路径配置
import allRouters from 'src/data/allRouters';

// antd message 全局配置
message.config({
    maxCount: 1,
});

const App = () => {
    return (
        <Router>
            <div className="App">
                <Routes routes={allRouters} />
            </div>
        </Router>
    );
};
export default App;


版权声明:本文为m0_38134431原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。