Axios Interceptors 401 React Native Expo

Hello can anyone help me, I have a problem about axios intercepotrs. When Error Status === 401 My Application (React Native) does not redirect to the signin page.

import axios from "axios";
import {BASE_URL} from '@env'
import { useNavigation } from "@react-navigation/native";
import * as SecureStore from 'expo-secure-store';

const TOKEN_KEY = "token"
const api = axios.create({
  baseURL: BASE_URL
})


api.interceptors.response.use(async (response) => {
    return response
  }, async function (error) {
    const navigation = useNavigation()
    if (error.response.status === 401 || error.response.status === 500) {
      api.defaults.headers.common['Authorization'] = ''
      await SecureStore.deleteItemAsync(TOKEN_KEY)
      navigation.navigate('SignIn')
    }
    return Promise.reject(error);
});

export {api}

Can anyone tell me how properly deal with the condition ?

Upvotes: 0

Views: 671

Answers (2)

Fathul Fahmy
Fathul Fahmy

Reputation: 21

If you encounter the issue of needing React hooks e.g. usePathname() in Axios interceptors but run into React’s rules of hooks error, you can resolve it by creating a React component. This approach leverages React states, effects, and hooks correctly while integrating them with Axios interceptors.

Example Solution

Below is an example of how to achieve this by wrapping your Axios interceptor in a React component:


import { PropsWithChildren, useEffect } from "react";

import { router, usePathname } from "expo-router";

import { useNotification } from "@/components/ui/notification";

import { api } from "./api";

export const AxiosInterceptor = ({ children }: PropsWithChildren) => {
  const route = usePathname();
  const unguardedRoutes = ["/login", "/register"];
  const isGuardedRoute = !unguardedRoutes.includes(route);

  useEffect(() => {
    /* ======================================== SUCCESS RESPONSE INTERCEPTOR */
    const responseInterceptor = (response: any) => {
      return response.data;
    };

    /* ======================================== ERROR RESPONSE INTERCEPTOR */
    const errorInterceptor = (e: any) => {
      /* ======================================== REDIRECT */
      const status = e.response?.status;
      const unauthenticatedStatus = [401, 403, 409];
      const isUnauthenticated = unauthenticatedStatus.includes(status);

      if (isGuardedRoute && isUnauthenticated) {
        router.replace("/login");
      }

      /* ======================================== RETURNS */

      return Promise.reject(e);
    };

    const interceptor = api.interceptors.response.use(responseInterceptor, errorInterceptor);

    return () => {
      api.interceptors.response.eject(interceptor);
    };
  }, [isGuardedRoute]);

  return children;
};


Usage

Wrap your root layout in the AxiosInterceptor component:


import { AxiosInterceptor } from "./AxiosInterceptor";

const RootLayout = () => {
  return (
    <AxiosInterceptor>
        <Stack>
            <Stack.Screen name="(tabs)" />
            <Stack.Screen name="(auth)" />
            <Stack.Screen name="+not-found" />
        </Stack>
    </AxiosInterceptor>
  );
}



Upvotes: 0

Nima Zarei
Nima Zarei

Reputation: 1238

You're using useNavigation, which is a hook, inside a function which is not a React component.

You can transform this into a custom hook or just use the interceptor inside your root (App.js) React component.

Upvotes: 0

Related Questions