DEV Community

Discussion on: React hooks in Axios interceptors

Collapse
 
iamyoki profile image
Yoki

useEffect is asynchronous to children's effect, so request call might be at the same time with interceptor setup and even sooner, in that case the interceptor might not work as we expected.
So the solution is set a state as false initially, and set it to true after interceptor setup complete, then we return the children

function AxiosIntercetor({children}) {
  const [isSet, setIsSet] = useState(false)

  useEffect(()=>{
    ...
    setIsSet(true)
  })

  return isSet && children
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mohaymenrafi profile image
mohaymen_rafi

yes, i got the same issue and tried your solution to return isSet && children, however typescirpt does not allow to do that.
How can I fix in that case?

Collapse
 
seyyed_sina profile image
Seyed Sina • Edited

@mohaymenrafi
try to wrap children in empty tag (i.e: or <></>) and it will work

Collapse
 
clefayomide profile image
clefayomide • Edited

i think a better approach is to prevent the request from running on initial render. this helped me, stackoverflow

Collapse
 
radandevist profile image
Andrianarisoa Daniel

@iamyoki 's answer is workin for me!! And rather simpler to understand.
I indeed tried preventing the request from running on initial render as you suggested but the interceptor stayed unsed for the following renders too.

Collapse
 
galih56 profile image
Galih indra

Damn you save my life, I'd been searching this issue for a full day. You are my hero

Collapse
 
devadossedison profile image
Edison Devadoss • Edited
import axios, {
  AxiosError,
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse
} from 'axios';
import environment from '../config/environment';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.min.css';

import { useSelector, useDispatch } from 'react-redux';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

const toastId: string = 'interceptor';

interface ErrorData {
  errors: string[];
}

const http = axios.create({
  baseURL: environment.baseURL
});

const parseErrorMessage = (errResponse: AxiosResponse<ErrorData>): string => {
  console.log('errResoponse', errResponse);
  return errResponse?.data ? errResponse.data?.errors[0] : '';
};

const AxiosInstance = ({ children }: any) => {
  const navigate = useNavigate();
  const [isSet, setIsSet] = useState(false);
  const token = useSelector((state: any) => state.auth.token);

  useEffect(() => {
    // needs to fix
    const reqInterceptor = (config: any) => {
      if (token) {
        if (config.headers) {
          config.headers['Authorization'] = `Bearer ${token}`;
        }
      }
      return config;
    };

    const resInterceptor = (response: AxiosResponse) => {
      return response.data;
    };

    const errInterceptor = (error: AxiosError) => {
      const message = parseErrorMessage(
        error.response as AxiosResponse<ErrorData>
      );
      if (message) {
        error.message = message;
      }
      const currentPath = window.location.pathname;
      if (error.response?.status === 401 && !currentPath.includes('login')) {
        console.log('in side', currentPath.includes('login'));
        if (!toast.isActive(toastId)) {
          toast.error(message, { toastId });
        }
        setTimeout(() => {
          // Clear token form db
          navigate('/login');
        }, 3000);
      }
      return Promise.reject(error);
    };

    const interceptorReq = http.interceptors.request.use(
      reqInterceptor,
      (error) => {
        console.log('interceptor req error', error);
      }
    );

    const interceptorRes = http.interceptors.response.use(
      resInterceptor,
      errInterceptor
    );

    setIsSet(true);
    return () => {
      http.interceptors.request.eject(interceptorReq);
      http.interceptors.response.eject(interceptorRes);
    };
  }, [isSet, navigate, token]);
  return isSet && children;
};

export default http;
export { AxiosInstance };

Enter fullscreen mode Exit fullscreen mode

This is my code. In the request interceptor is not call all the time. After login, useEffect of the next screen it makes an API call in that call it should set header from redux. But it is not working. Where is the mistake I make?

Collapse
 
saharakmanoo profile image
Saharak

You are my hero, Thank you very much