0

I have this a Dashboard component which makes three API calls to fetch widget data.

If any API call fails it refreshes token.

But, when Dashboard renders it makes API call individually and they don't wait to check if first api call failed or token is refreshed. Each api call ends up making another call to refresh the token.

It should stop at first API call fail and refresh the token.

But it does so for each request. How can I prevent this behaviour.

It seems I need to make request sequentially.

const Dashboard = () => {
  const { response: studentResponse } = useAxios(ApiConfig.STUDENT.GET_STUDENTS);
  const { response: courseResponse } = useAxios(ApiConfig.COURSE.GET_COURSES);
  const { response: feesResponse } = useAxios(ApiConfig.FEES.GET_TOTAL);

  return (
    <Box padding={2} width="100%">
      <Stack direction={'row'} justifyContent="space-between" gap={2} mb={10}>
        <NewWidget type={'student'} counter={studentResponse?.data?.length} />
        <NewWidget type={'course'} counter={courseResponse?.data?.length} />
        <NewWidget type={'earning'} counter={feesResponse?.data} />
      </Stack>
    </Box>
  );
};
export default Dashboard;

use-axios.js

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

import history from '../utils/history';
import refreshToken from './refresh-token';

const Client = axios.create();

Client.defaults.baseURL = 'http://localhost:3000/api/v1';

const getUser = () => {
  const user = localStorage.getItem('user');
  return user ? JSON.parse(user) : null;
};

const updateLocalStorageAccessToken = (accessToken) => {
  const user = getUser();
  user.accessToken = accessToken;
  localStorage.setItem('user', JSON.stringify(user));
};

Client.interceptors.request.use(
  (config) => {
    const user = getUser();
    config.headers.Authorization = user?.accessToken;
    return config;
  },
  (error) =>
    // Do something with request error
    Promise.reject(error)
);

Client.interceptors.response.use(
  (response) => response,
  (error) => {
    // Reject promise if usual error
    if (error.response.status !== 401) {
      return Promise.reject(error);
    }
    const user = getUser();

    const status = error.response ? error.response.status : null;

    const originalRequest = error.config;
    console.log(originalRequest);

    if (status === 401 && originalRequest.url !== '/auth/refresh-token') {
      refreshToken(user.refreshToken)
        .then((res) => {
          const { accessToken } = res.data.data;
          Client.defaults.headers.common.Authorization = accessToken;
          // update local storage
          updateLocalStorageAccessToken(accessToken);
          return Client(originalRequest);
        })
        .catch((err) => {
          console.log(err);
          if (err.response.status === 401) {
            localStorage.setItem('user', null);
            history.push('/login');
          }
          return Promise.reject(err);
        });
    }
    history.push('/login');
    return Promise.reject(error);
  }
);

export const useAxios = (axiosParams, isAuto = true) => {
  const [response, setResponse] = useState(undefined);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await Client.request({
        ...params,
        method: params.method || 'GET',
        headers: {
          accept: 'application/json',
        },
      });
      setResponse(result.data);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (isAuto) fetchData(axiosParams);
  }, [axiosParams, isAuto]); // execute once only

  return { fetch: () => fetchData(axiosParams), response, error, loading };
};

confusedWarrior
  • 938
  • 2
  • 14
  • 30

1 Answers1

0

In the interceptor for the response, you check if there's an error. I would keep a state which contains the previous success of a call, implement that how you wish - after that, create an interceptor for requests which checks if that error occurred, and if so, cancel the request:

axios.interceptors.request.use((req: AxiosRequestConfig) => {
    if(error){
      throw new axios.Cancel('Operation canceled due to previous failure.');
    }
    else {
        return req
    }
})

Also see: Axios: how to cancel request inside request interceptor properly?