【问题标题】:How to cancel Axios request in react redux with hooks?如何在带有钩子的react redux中取消Axios请求?
【发布时间】:2020-10-28 23:10:34
【问题描述】:

我正在使用 Redux 在 React 中工作。我已经编写了名为 products 的操作,以使用 Axios 从后端获取产品详细信息。其中,如果用户在此过程中导航到另一个页面,我使用取消令牌来取消 HTTP 请求。

但是,当我导航到另一个页面时,我仍然在控制台中收到如下错误。

index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

当我导航到同一产品页面以获取数据时,请求会转到 catch 块以引发错误。我使用下面的代码来调度操作。

product.js 作为操作

const source = Axios.CancelToken.source();
const fetchProducts = () => {
  return async (dispatch) => {
    try {
      const response = await Axios.get("myurl", {
        cancelToken: source.token,
      });

      if (response.status !== 200) {
        throw new Error("Something went wrong, while fetching the products!");
      }

      dispatch({ type: GET_PRODUCTS, products: response.data });
    } catch (err) {
      if (Axios.isCancel(err)) {
        console.log(err.message);
      } else {
        throw err;
      }
    }
  };
};

const cancelRequest = () => {
  return (dispatch) => {
    if (source !== typeof undefined) {
      source.cancel("Operation canceled by the user.");
      dispatch({ type: CANCEL_REQUEST, message: "Request canceled by user!" });
    }
  };
};

组件文件:

const loadProducts = useCallback(async () => {
  setError(null);
  try {
    await dispatch(productActions.fetchProducts());
  } catch (err) {
    setError(err.message);
  }
}, [dispatch, setError]);

useEffect(() => {
  setIsLoading(true);
  loadProducts().then(() => {
    setIsLoading(false);
  });

  return () => {
    dispatch(productActions.cancelRequest());
  };
}, [dispatch, loadProducts, setIsLoading]);

如何解决这个问题?

【问题讨论】:

    标签: reactjs react-redux axios


    【解决方案1】:

    您正在取消对unmount 的请求。取消后,您将进入loadProducts 的捕获块。在那里,您正在设置状态setError(...)。因此,当您尝试在未安装的组件上设置状态时,您会收到错误消息。

    要解决这个问题,只需不要在 catch 块中设置状态。根据您的代码,似乎不需要设置错误状态。如果您愿意,可以从商店购买(因为减速器 CANCEL_REQUEST 应该有消息)

    const loadProducts = useCallback(async () => {
      setError(null);
      try {
        await dispatch(productActions.fetchProducts());
      } catch (err) {
        //setError(err.message); //<----- this is the issue, remove this line
      }
    }, [dispatch, setError]);
    
    useEffect(() => {
      setIsLoading(true);
      loadProducts().then(() => {
        setIsLoading(false);
      });
    
      return () => {
        dispatch(productActions.cancelRequest());
      };
    }, [dispatch, loadProducts, setIsLoading]);
    

    编辑基于 cmets:

    进行以下更改,它可以正常工作。

    • 先放then块,然后放catch
    • 在 promise-catch 块中移动设置状态(不是 try catch)
    • 此外,我们在 promise-catch 块中处理 setError 时不需要 try/catch 块

    Working demo

    代码 sn-p

    const useAxiosFetch = url => {
      const [data, setData] = useState(null)
      const [error, setError] = useState(null)
      const [loading, setLoading] = useState(false)
    
      useEffect(() => {
        let source = axios.CancelToken.source()
        setLoading(true)
        axios
          .get(url, {
            cancelToken: source.token,
          })
          .then(a => {
            console.log('here')
            setData(a)
            setLoading(false)
          })
          .catch(function(thrown) {
            //put catch after then
            if (axios.isCancel(thrown)) {
              console.log(`request cancelled:${thrown.message}`)
            } else {
              console.log('another error happened')
              setData(null)
              setError(thrown)
            }
            // throw (thrown)
          })
    
        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}
    }
    

    【讨论】:

    • 还是不行。我删除了 setError 方法。但没有运气,我遇到了同样的错误。
    • 每当我导航到同一页面以获取数据时,它都会进入运行中的 catch 块。捕捉(错误){如果(Axios.isCancel(错误)){控制台.日志(错误消息); } 其他 { 抛出错误; } }
    • 嗨 - 它应该可以工作......如果可能的话,你能分享你的完整代码吗(codesandbox或github)......还有see an example here
    • 您好,请到GitHub路径找源码。 github.com/spvjebaraj/react-redux-axios-cancel
    • 我还在codesandbox.io/s/8lzwl57nr8?file=/src/index.js 的代码沙箱中测试了这个场景。它也失败了。
    猜你喜欢
    • 2017-10-14
    • 2017-12-11
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2020-01-27
    • 2019-04-24
    • 2021-09-05
    相关资源
    最近更新 更多