AuthContext

Dead-simple AuthContext provider

Literally the whole logic

// authContext.tsx
import {AxiosInstance, AxiosRequestConfig} from 'axios';
import {
  createContext,
  FC,
  useCallback,
  useContext,
  useMemo,
  useState,
} from 'react';
import {queryCache} from 'react-query';

import createClient from '../utils/apiClient';
import {getAccessToken} from '../utils/authService';
import {getToken, removeToken, setToken} from '../utils/jwtTokenUtils';

interface User {
  token?: string;
}

interface AuthContextProps {
  user: User;
  signIn: (token) => void;
  signOut: () => void;
}

const AuthContext = createContext({} as IAuthContext);

const AuthProvider: FC = (props) => {
  const [user, setUser] = useState<IUser>(() => {
    const token = getToken();
    return {token};
  });

  const signIn = useCallback(async () => {
    // implement this
  }, [setUser]);

  const signOut = useCallback(() => {
    queryCache.clear();
    removeToken();
    setUser({});
  }, [setUser]);

  const value = useMemo(() => ({user, signIn, signOut}), [
    signIn,
    signOut,
    user,
  ]);

  return <AuthContext.Provider value={value} {...props} />;
};

const useAuth = (): AuthContextProps => {
  const context = useContext(AuthContext);
  return context;
};

const useClient = (): AxiosInstance => {
  const {user, signOut} = useAuth();
  const token = user?.token;

  const client = useMemo(
    () => createClient({authToken: token, onUnauthorized: signOut}),
    [token]
  );

  return useCallback((config: AxiosRequestConfig) => client(config), []);
};

export {AuthProvider, useAuth, useClient};

Adding wrappers

const AppProviders: FC = ({children}) => {
  return (
    <StrictMode>
      <ChakraProvider>
        <ReactQueryConfigProvider config={queryConfig}>
          <BrowserRouter>
            <AuthProvider>{children}</AuthProvider>
          </BrowserRouter>
        </ReactQueryConfigProvider>
      </ChakraProvider>
    </StrictMode>
  );
};

const Root: FC = () => {
  return (
    <AppProviders>
      <App />
    </AppProviders>
  );
};

Simple fork

const App: FC = () => {
  const {user} = useAuth();
  return user.token ? <AuthenticatedApp /> : <UnauthenticatedApp />;
};

Requesting resources

// using `useClient`
const useGetAllHabitsQuery = (): {status: QueryStatus; data: unknown} => {
  const client = useClient();

  const request = useCallback((key) => {
    return client({url: 'v1/habits', cancelToken: getCancelToken(key)});
  }, []);

  const {data, status} = useQuery({queryKey, queryFn: request});

  return {data, status};
};