1

I need to add the access token to HTTP requests.

My approach is to use useEffect to listen for changes in the auth object and respond by setting up an axios interceptor -- and removing a previos interceptor if there was one (because the token value it had will have expired).

import { InternalAxiosRequestConfig } from "axios";
import { AuthContextProps, useAuth } from "react-oidc-context";
import { AXIOS_INSTANCE } from "./api_mutator";
import { AppSettings, useAppSettings } from "./appSettings";

export const ApiContext: Context<null | string> = createContext<null | string>(null)

export const ApiProvider: React.FC<{ children: JSX.Element | JSX.Element[] }> = ({ children }) => {
    let appSettings: null | AppSettings = useAppSettings();
    let auth: AuthContextProps = useAuth();

    const [access_token, setAccesToken] = useState<null | string>(null)
    const [axiosInterceptorId, setAxiosInterceptorId] = useState<null | number>(null)

    useEffect(() => {
        console.log('ApiProvider.useEffect'); // This stops lots of refreshing.

        if (axiosInterceptorId) {
            AXIOS_INSTANCE.interceptors.request.eject(axiosInterceptorId);
        }

        let new_access_token: string | null = null;
        if (((appSettings?.apiBaseUrl?? '') !== '') && (auth?.isAuthenticated ?? false) && auth.user?.access_token) {
            new_access_token = auth.user.access_token;
            setAccesToken(new_access_token);
        }

        if (new_access_token) {
            // We're good: set up the interceptor.
            const interceptorId: number = AXIOS_INSTANCE.interceptors.request.use((config: InternalAxiosRequestConfig<any>) => {
                return {
                    ...config,
                    baseURL: appSettings!.truthPortalApi!,
                    headers: auth.user!.access_token!
                        ? {
                            ...config.headers,
                            Authorization: `Bearer ${auth.user!.access_token!}`,
                        }
                        : config.headers,
                } as InternalAxiosRequestConfig<any>;
            });
            setAxiosInterceptorId(interceptorId);
        }
        else {
            setAxiosInterceptorId(null);
        }

    }, [appSettings, auth]);

    return (
        <ApiContext.Provider value={access_token}>
            {children}
        </ApiContext.Provider>
    )
}

// Hook for consumers to know whether the API is available.
export function useApi(): boolean {
    let at: string | null = useContext(ApiContext);
    return at != null && at !== '';
}

The first problem is that this causes hundreds of page reloads. I found that I could stop this by adding the console.log.

I've been told that I don't need to useEffect -- but not why it's unnecessary or what to do instead. I believe that I need to useEffect beause I need to listen for changes in the auth object -- otherwise I'd only see its initial value and I'd never see an updated token.

If I run the code as part of the function componenent and not inside useEffect then the screen goes white and I get the infinite loop error.

What's going on?!

Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54

0 Answers0