I am trying to set up AuthContext is react and running into a problem where react calls an API before axios sets the auth headers in the request. This is causes API failures on first render missing authentication parameters
.
I will provide smaller examples of AuthContext
, AuthService
, and App
AuthContext
import React, { createContext, useState, useEffect, useContext, useCallback } from "react";
import AuthService from "./authService";
// Hardcoded token for testing
const hardcodedToken = "your-hardcoded-token-here";
// Create the AuthContext
const AuthContext = createContext(null);
export const AuthContextProvider = ({ children }) => {
const [token, setToken] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const checkIfLogged = useCallback(async () => {
const token = hardcodedToken;
if (token) {
setToken(token);
setIsAuthenticated(true);
// Set Axios defaults and mark auth as ready
authService.setAxiosDefaults(token);
} else {
setIsAuthenticated(false);
authService.TODOLogout();
}
}, []);
useEffect(() => {
checkIfLogged();
}, [checkIfLogged]);
return (
<AuthContext.Provider value={{ token, isAuthenticated }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
AuthService
import axios from "axios";
class AuthService {
constructor(authRedirectUrlPrefix) {
this.authRedirectUrlPrefix = authRedirectUrlPrefix;
}
/** Set default axios headers and interceptors */
setAxiosDefaults(token) {
debugger;
// Pass auth token in each request
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.interceptors.response.use(
(response) => {
return response; // Ignore successful responses
},
(error) => {
// If response contains auth error, redirect to login
if (error.response.status === 401) {
this.redirectToLogout(false);
}
return error;
}
);
}
}
export default AuthService;
App
import "./App.css";
import { Route, Switch, Redirect, BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import Container from "@mui/material/Container";
import Header from "./layout/Header";
import store from "./store";
import { Home } from "./components/Home";
import { AuthContextProvider } from "./auth/AuthContext";
import { ApiContextProvider } from "./context/ApiContext";
const mdTheme = createTheme();
function App() {
return (
<AuthContextProvider>
<ApiContextProvider>
<BrowserRouter>
<ThemeProvider theme={mdTheme}>
<Box sx={{ display: "flex" }}>
<CssBaseline />
<Header />
<Box
component="main"
sx={{
backgroundColor: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[300]
: theme.palette.grey[900],
flexGrow: 1,
height: "100vh",
overflow: "auto",
}}
>
<Toolbar />
<Container maxWidth="lg" sx={{ mt: 3, mb: 3 }}>
<Switch>
<Route exact path="/" component={Home} />
<Redirect from="*" to="/" />
</Switch>
</Container>
</Box>
</Box>
</ThemeProvider>
</BrowserRouter>
</ApiContextProvider>
</AuthContextProvider>
);
}
export default App;
Now given all of that set up, what’s happening is my react app calls this first api request before the auth headers are set. I can tell because I added a debugger
here and this is hit first when reloading the app.
import React, { createContext, useContext, useMemo } from "react";
import axios from "axios";
// import config from "../config";
const ApiClient = {
getData: async () => {
// debugger;
const response = await axios.get(/demo/data);
return response.data;
},
};
// Create the API Client Context
export const ApiClientContext = createContext(ApiClient);
export const useApiClient = () => {
return useContext(ApiClientContext);
};
// Provider component to wrap around your app
export const ApiContextProvider = ({ children }) => {
const value = useMemo(() => {
return ApiClient;
}, []);
return (
<ApiContextProvider.Provider value={value}>
{children}
</ApiContextProvider.Provider>
);
};