howard wolowitz
howard wolowitz

Reputation: 628

How to cancel axios requests

I have api file with requests

import * as axios from "axios";

export const productAPI = {
getProducts() {
    return axios({
        method: 'get',
        url: `/api/products`
    });
}

};

which reaches to transport.js and sends request(i think that part is not important).
Method above is called from my component like this

 useEffect(()=> {
    setLoading(true);
    productAPI.getProducts()
        .then((response) => {
            if(response.status === 200) {
                history.push(`${pathWithLocation}${PAGES.newLoan}`);
            }
        })
        .catch((error) => {
            if (error.response.data.error.message) {
                dispatch(addModal({
                    type: 'basic',
                    size: 'middle',
                    title: 'some title',
                    text: error.response.data.error.message,
                    buttons: [{ buttonLabel: 'ОК', onClick: ()=> dispatch(removeModal()) }]       
                }))
            }
        })
        .finally(() => {
            setLoading(false);
        });
},[])

I want to cancel this specific request when component is unmounted. (switched route for example)

Upvotes: 0

Views: 799

Answers (2)

Dmitriy Mozgovoy
Dmitriy Mozgovoy

Reputation: 1597

getProducts(cancelToken) {
    return axios({
        method: 'get',
        url: `/api/products`,
        cancelToken
    });
}

 useEffect(()=> {
    const source= CancelToken.source();
    const isMounted= true;

    setLoading(true);
    productAPI.getProducts(source.token)
        .then((response) => {
            if(response.status === 200) {
                history.push(`${pathWithLocation}${PAGES.newLoan}`);
            }
        })
        .catch((error) => {
            if (error.response.data.error.message) {
                dispatch(addModal({  
                  ...
                }))
            }
        })
        .finally(() => {
            isMounted && setLoading(false);
        });
    return ()=>{
      isMounted= false;
      source.cancel();
    }
},[])

Or a bit magic way (Codesandbox demo):

import React, { useState } from "react";
import {
  useAsyncEffect,
  CanceledError,
  E_REASON_UNMOUNTED
} from "use-async-effect2";
import cpAxios from "cp-axios";

export default function TestComponent(props) {
  const [text, setText] = useState("");
  const [loading, setLoading] = useState(true);

  const cancel = useAsyncEffect(
    function* () {
      try {
        const response = yield cpAxios(props.url);
        setText(JSON.stringify(response.data));
        setLoading(false);
        if (response.status === 200) {
          //history.push(`${pathWithLocation}${PAGES.newLoan}`);
        }
      } catch (err) {
        CanceledError.rethrow(err, E_REASON_UNMOUNTED);
        setLoading(false);
        setText(err.toString());
        //dispatch(addModal({})
      }
    },
    [props.url]
  );

  return (
    <div className="component">
      <div className="caption">useAsyncEffect demo:</div>
      <div>{text}</div>
      <button onClick={cancel} disabled={!loading}>
        Cancel request
      </button>
    </div>
  );
}

Upvotes: 0

Martin
Martin

Reputation: 6146

You can just use a isCurrent flag. (I have to admit that I have not considered what the benefit of using the axios.cancelToken mechanism would be here. Maybe it would make it cleaner, maybe it would just make it more convoluted.)

useEffect(() => {
    const isCurrent = true;
    setLoading(true);
    productAPI.getProducts()
        .then((response) => {
            if(isCurrent && response.status === 200) {
                history.push(`${pathWithLocation}${PAGES.newLoan}`);
            }
        })
        .catch((error) => {
            if (isCurrent && error.response.data.error.message) {
                dispatch(addModal({/*...*/}))
            }
        })
        .finally(() => {
            if (isCurrent) setLoading(false);
        });
    return () => { isCurrent = false };
}, [])

Upvotes: 1

Related Questions