4

In ReactJs I am using Axios to getting data from API. I need to use cancelToken when I try to make the duplicate requests. E.g: suppose I am on the homepage before complete Axios request, I am requested for About page. As a result, the React app showing memory leaking error. So, my plan is to set Axios cancelToken in Axios interceptors. I have tried but, it is not working for me.

requestApi.js

import axios from 'axios';

const requestApi = axios.create({
  baseURL: process.env.REACT_APP_API_URL
});
const source = axios.CancelToken.source();

requestApi.interceptors.request.use(async config => {
  const existUser = JSON.parse(localStorage.getItem('user'));
  const token = existUser && existUser.token ? existUser.token : null;
  if (token) {
    config.headers['Authorization'] = token;
    config.headers['cache-control'] = 'no-cache';
  }

  config.cancelToken = source.token;

  return config;
}, error => {
  return Promise.reject(error);
});

requestApi.interceptors.request.use(async response => {
  throw new axios.Cancel('Operation canceled by the user.');
  return response;
}, error => {
  return Promise.reject(error);
});

export default requestApi;

Dashboard.js

import requestApi from './requestApi';

useEffect(() => {
  const fetchData = async () => {
    try {
      const res = await requestApi.get('/dashboard');
      console.log(res.data);
    } catch (error) {
      console.log(error);
    }
  }

  fetchData();
}, []);
Sonjoy Datta
  • 1,188
  • 2
  • 13
  • 21

2 Answers2

4

in case you still need it or if someone else comes looking for this. This is how it has worked for me.

import axios from "axios";

// Store requests
let sourceRequest = {};

const requestApi = axios.create({
  baseURL: process.env.REACT_APP_API_URL
});

requestApi.interceptors.request.use(
  async config => {
    const existUser = JSON.parse(localStorage.getItem("user"));
    const token = existUser && existUser.token ? existUser.token : null;
    if (token) {
      config.headers["Authorization"] = token;
      config.headers["cache-control"] = "no-cache";
    }

    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

requestApi.interceptors.request.use(
  request => {
    // If the application exists cancel
    if (sourceRequest[request.url]) {
      sourceRequest[request.url].cancel("Automatic cancellation");
    }

    // Store or update application token
    const axiosSource = axios.CancelToken.source();
    sourceRequest[request.url] = { cancel: axiosSource.cancel };
    request.cancelToken = axiosSource.token;

    return request;
  },
  error => {
    return Promise.reject(error);
  }
);

export default requestApi;
0

This may not be a stable solution, but we can use some magic to make a component that terminates async code (including requests) running inside when the component unmounts. No tokens required to make it work. See a Live Demo

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

function* makeAPICall(url) {
  const existUser = JSON.parse(localStorage.getItem("user"));
  const token = existUser && existUser.token ? existUser.token : null;
  return yield cpAxios(url, {
    headers: {
      Authorization: token,
      "cache-control": "no-cache"
    }
  });
}

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

  const cancel = useAsyncEffect(
    function* () {
      console.log("mount");

      this.timeout(props.timeout);

      try {
        setText("fetching...");
        const response = yield* makeAPICall(props.url);
        setText(`Success: ${JSON.stringify(response.data)}`);
      } catch (err) {
        CanceledError.rethrow(err, E_REASON_UNMOUNTED); //passthrough
        setText(`Failed: ${err}`);
      }

      return () => {
        console.log("unmount");
      };
    },
    [props.url]
  );

  return (
    <div className="component">
      <div className="caption">useAsyncEffect demo:</div>
      <div>{text}</div>
      <button onClick={cancel}>Abort</button>
    </div>
  );
}
Dmitriy Mozgovoy
  • 1,419
  • 2
  • 8
  • 7