How to logout user when token expires in react app

The issue you are facing is simple. Your AuthReducer takes in the initialState only once when its created. Now when you reload your app, everything is initialized again and the expiry is taken care of by your logic. However on Route change It doesn't re-evaluate your initialState.

However what you can do is while using setContext you can check for validation of expiry by decoding the token using jwtDecode and refresh the token if it expired and save in localStorage since this is executed on every request

const authLink = setContext(async () => {
  let token = localStorage.getItem('JWT_Token')
  const { exp } = jwtDecode(token)
  // Refresh the token a minute early to avoid latency issues
  const expirationTime = (exp * 1000) - 60000
  if (Date.now() >= expirationTime) {
    token = await refreshToken()
    // set LocalStorage here based on response;
  }
  return {
    // you can set your headers directly here based on the new token/old token
    headers: {
      ...
    }
  }
})

However since you wish to redirect to login page and not refresh token when the token expired you can make use of custom history object with Routes

src/history.js

import { createBrowserHistory } from 'history';
const history = createBrowserHistory()
export default history;

App.js

import history from '/path/to/history.js';
import { Router } from 'react-router-dom';

<AuthProvider>
  <Router history={history}>
    <div className="App wrapper">
      <Routes/>
    </div>
  </Router>
</AuthProvider>

and then in setContext you could do

import history from '/path/to/history';
const authLink = setContext(async () => {
  let token = localStorage.getItem('JWT_Token')
  const { exp } = jwtDecode(token)
  const expirationTime = (exp * 1000) - 60000
  if (Date.now() >= expirationTime) {
    localStorage.clear();
    history.push('/login');
  }
  return {
    // you can set your headers directly here based on the old token
    headers: {
      ...
    }
  }
})

For your your problem the solution might be like:

  • Remove the auth part from the context. (Bad practice)
  • Create a component with react-router subscribed to check the auth state of the user.
  • Render it in the main component.

authverify.component.js

import { withRouter } from "react-router-dom";

const AuthVerifyComponent = ({ history }) => {
  history.listen(() => {  // <--- Here you subscribe to the route change
    if (localStorage.getItem("JWT_Token")) {
      const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
      console.log(jwt_Token_decoded.exp * 1000);
      console.log(Date.now());
      if (jwt_Token_decoded.exp * 1000 < Date.now()) {
        localStorage.clear();
      } else {
        initialstate.user = jwt_Token_decoded;
      }
    }
  });
  return <div></div>;
};

export default withRouter(AuthVerifyComponent);

app.js

<AuthProvider>
  <Router>
    <div className="App wrapper">
      <Routes />
      <AuthVerifyComponent />
    </div>
  </Router>
</AuthProvider>;