在React Hooks use中取消Axios REST调用效果清除失败

Pet*_*ner 6 reactjs axios react-hooks

我显然不能正确清理并取消axios GET请求。在我当地,我收到一条警告,内容是

无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要修复,请取消使用useEffect清理功能中的所有订阅和异步任务。

在stackblitz上,我的代码有效,但是由于某些原因,我无法单击按钮来显示错误。它只是始终显示返回的数据。

https://codesandbox.io/s/8x5lzjmwl8

请查看我的代码并找到我的缺陷。

useAxiosFetch.js

import {useState, useEffect} from 'react'
import axios from 'axios'

const useAxiosFetch = url => {
    const [data, setData] = useState(null)
    const [error, setError] = useState(null)
    const [loading, setLoading] = useState(true)

    let source = axios.CancelToken.source()
    useEffect(() => {
        try {
            setLoading(true)
            const promise = axios
                .get(url, {
                    cancelToken: source.token,
                })
                .catch(function (thrown) {
                    if (axios.isCancel(thrown)) {
                        console.log(`request cancelled:${thrown.message}`)
                    } else {
                        console.log('another error happened')
                    }
                })
                .then(a => {
                    setData(a)
                    setLoading(false)
                })
        } catch (e) {
            setData(null)
            setError(e)
        }

        if (source) {
            console.log('source defined')
        } else {
            console.log('source NOT defined')
        }

        return function () {
            console.log('cleanup of useAxiosFetch called')
            if (source) {
                console.log('source in cleanup exists')
            } else {
                source.log('source in cleanup DOES NOT exist')
            }
            source.cancel('Cancelling in cleanup')
        }
    }, [])

    return {data, loading, error}
}

export default useAxiosFetch
Run Code Online (Sandbox Code Playgroud)

index.js

import React from 'react';

import useAxiosFetch from './useAxiosFetch1';

const index = () => {
    const url = "http://www.fakeresponse.com/api/?sleep=5&data={%22Hello%22:%22World%22}";
    const {data,loading} = useAxiosFetch(url);

    if (loading) {
        return (
            <div>Loading...<br/>
                <button onClick={() => {
                    window.location = "/awayfrom here";
                }} >switch away</button>
            </div>
        );
    } else {
        return <div>{JSON.stringify(data)}xx</div>
    }
};

export default index;
Run Code Online (Sandbox Code Playgroud)

Moh*_*taz 13

根据 Axios文档, cancelToken 已弃用,从 v0.22.0 开始,Axios 支持 AbortController 以 fetch API 方式取消请求:

    //...
React.useEffect(() => {
    const controller = new AbortController();
    axios.get('/foo/bar', {
    signal: controller.signal
    }).then(function(response) {
     //...
     }).catch(error => {
        //...
     });
    return () => {
      controller.abort();
    };
  }, []);
//...
Run Code Online (Sandbox Code Playgroud)


Pet*_*ner 5

这是最终代码,一切正常,以防其他人回来。

import {useState, useEffect} from "react";
import axios, {AxiosResponse} from "axios";

const useAxiosFetch = (url: string, timeout?: number) => {
    const [data, setData] = useState<AxiosResponse | null>(null);
    const [error, setError] = useState(false);
    const [errorMessage, setErrorMessage] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        let unmounted = false;
        let source = axios.CancelToken.source();
        axios.get(url, {
            cancelToken: source.token,
            timeout: timeout
        })
            .then(a => {
                if (!unmounted) {
                    // @ts-ignore
                    setData(a.data);
                    setLoading(false);
                }
            }).catch(function (e) {
            if (!unmounted) {
                setError(true);
                setErrorMessage(e.message);
                setLoading(false);
                if (axios.isCancel(e)) {
                    console.log(`request cancelled:${e.message}`);
                } else {
                    console.log("another error happened:" + e.message);
                }
            }
        });
        return function () {
            unmounted = true;
            source.cancel("Cancelling in cleanup");
        };
    }, []);

    return {data, loading, error, errorMessage};
};

export default useAxiosFetch;
Run Code Online (Sandbox Code Playgroud)

  • 如果请求没有取消或者其他一些任务无法取消,则需要卸载。如果您减慢网络速度并在网络调用返回之前卸载组件,您将看到错误“无法对已卸载的组件执行 React 状态更新”。 (2认同)