Handling the end of a user's session gracefully is just as important as logging them in. When a user intentionally clicks logout, the application must immediately clear the stored token from localStorage and reset the global state to null, instantly reflecting the unauthenticated state across every component. However, sessions can also end unexpectedly when a JWT expires—modern tokens often include an exp claim that the server checks, returning a 401 HTTP status code if the time has passed. Our Axios response interceptor detects this specific 401 code, clears the stale token, and redirects the user to the login page, often displaying a friendly notification that their session has timed out. This proactive approach prevents dreaded "broken UI" scenarios where components try to fetch data with invalid credentials, ensuring the user is always met with a clear path back to regaining access.
The Logout Function (in AuthContext)
// context/AuthContext.jsx (inside the provider)
const logout = () => {
localStorage.removeItem("accessToken");
localStorage.removeItem("user");
setUser(null);
// Optionally navigate using useNavigate if called inside a component
};
Handling Expiry with a Notification
We can enhance our Axios response interceptor to show a toast notification before redirecting, using a library like react-hot-toast.
// utils/axiosInstance.js
import toast from "react-hot-toast";
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
toast.error("Your session has expired. Please login again.");
localStorage.removeItem("accessToken");
localStorage.removeItem("user");
// Dispatch a custom event to let React Router know to redirect
window.dispatchEvent(new CustomEvent("unauthorized"));
}
return Promise.reject(error);
}
);
Then, in your App.jsx, you listen for this event and navigate programmatically:
// App.jsx (inside the component)
useEffect(() => {
const handleUnauthorized = () => {
navigate("/login", { replace: true });
};
window.addEventListener("unauthorized", handleUnauthorized);
return () => window.removeEventListener("unauthorized", handleUnauthorized);
}, [navigate]);