react-auth-kit: Warning: Cannot update a component while rendering a different component

Description
Warning message when accessing a secured page without beeing logged in.

react-dom.development.js:86 Warning: Cannot update a component (`AuthProvider`) while rendering a different component (`RequireAuth`). To locate the bad setState() call inside `RequireAuth`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
    at RequireAuth (http://localhost:3000/static/js/bundle.js:11361:21)
    at RenderedRoute (http://localhost:3000/static/js/bundle.js:39951:5)
    at Routes (http://localhost:3000/static/js/bundle.js:40394:5)
    at Router (http://localhost:3000/static/js/bundle.js:40332:15)
    at BrowserRouter (http://localhost:3000/static/js/bundle.js:38650:5)
    at RoutesComponent
    at AuthProvider (http://localhost:3000/static/js/bundle.js:11272:21)
    at App
printWarning @ react-dom.development.js:86
error @ react-dom.development.js:60
warnAboutRenderPhaseUpdatesInDEV @ react-dom.development.js:27492
scheduleUpdateOnFiber @ react-dom.development.js:25498
dispatchReducerAction @ react-dom.development.js:17452
isAuth @ PrivateRoute.tsx:44
RequireAuth @ PrivateRoute.tsx:50
renderWithHooks @ react-dom.development.js:16305
mountIndeterminateComponent @ react-dom.development.js:20074
beginWork @ react-dom.development.js:21587
beginWork$1 @ react-dom.development.js:27426
performUnitOfWork @ react-dom.development.js:26557
workLoopSync @ react-dom.development.js:26466
renderRootSync @ react-dom.development.js:26434
performSyncWorkOnRoot @ react-dom.development.js:26085
flushSyncCallbacks @ react-dom.development.js:12042
flushSyncCallbacksOnlyInLegacyMode @ react-dom.development.js:12021
batchedUpdates$1 @ react-dom.development.js:26148
batchedUpdates @ react-dom.development.js:3991
dispatchEventForPluginEventSystem @ react-dom.development.js:9287
dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465
dispatchEvent @ react-dom.development.js:6457
dispatchDiscreteEvent @ react-dom.development.js:6430

How to reproduce(required)
Detailed steps to reproduce the behaviour you experienced

  1. Clone the react-auth-kit repo
  2. Go to examples/create-react-app/src
  3. yarn install && yarn start
  4. in the browser navigate to localhost:3000/secure without logging in or login and logout
  5. See the error in the console (i have exactly the same behaviour in my real app)

Correct behaviour
No Warning should be thrown under normal and expected operation

Possible reason
Calling context.dispatch in PrivateRoute.tsx line 44 causes a state to be updated by the useReducer hook which leads to the warning.

Screenshots
image

Desktop (please complete the following information):

  • OS: Windows
  • Browser Chrome
  • Version latest
  • node 18.13

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 3
  • Comments: 26 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Temporary fix thanks to @federicoguti01 and @davidbro-in

const App = () => {
    //https://github.com/react-auth-kit/react-auth-kit/issues/1023
    const PrivateRoute = ({ Component }) => {
        const isAuthenticated = useIsAuthenticated();
        const auth = isAuthenticated();
        return auth ? <Component /> : <Navigate to="/login" />;
    };

    return (
        <AuthProvider authType={'cookie'}
            authName={'_auth'}
            cookieDomain={window.location.hostname}
            cookieSecure={window.location.protocol === "https:"}>
            <BrowserRouter>
                <div className="App">
                    <Navigation />
                    <Routes>
                        <Route path="/login" element={<Login />} />
                        <Route path="/home" element={<PrivateRoute Component={Home} />}></Route>
                    </Routes>
                </div>
            </BrowserRouter>
        </AuthProvider>
    )
}
export default App

The issue seems to be that during the redirect two components are being rendered (and having state changed) at the same time. In my case I was able to work around this by deferring the redirect until the current component has finished a render:

if (loggedIn) {
    setTimeout(() => navigate(redirectTo ?? '/'));
    return <></>;
  }

For me what worked was setting a object to authState in signIn function param

Adding a setTimeout for the route push resolution for me.

 if (!session) {
    setTimeout(() => {
      router.push("/login");
    }, 100);

    return null;
  }

This issue is coming for the signOut operation. As the signout operation is responsible for a rerender and Navigate to login is taking place at the same time, the error is coming.

Ref: https://github.com/react-auth-kit/react-auth-kit/blob/792dad79c4844d5f02dda2c92cf188cf3934c6c8/src/PrivateRoute.tsx#L58

React Auth Kit v3 solves this issue. To solve the issue please use any release after v3. Thanks!

const PrivateRoute = ({ YourProtectedComponent }) => {
    const isAuthenticated = useIsAuthenticated();
    const auth = isAuthenticated();

    const authHeader = useAuthHeader();
    const jwtAuthToken = authHeader();
    
    if (!isTokenExpired(jwtAuthToken) && auth) {
        return YourProtectedComponent;
    }
    
    return <Navigate to="/login" />
};

function isTokenExpired(jwtAuthToken) {
    if (!jwtAuthToken) return false; // does not exist
    const encodedPart = jwtAuthToken.split(' ')[1]; // "Bearer eyJhbGciOiJ..."
    const decodedToken = jwt_decode(encodedPart); // using jwt_decode lib
 
    if (decodedToken.exp === undefined) return true;
    
    const expirationTimeMilliseconds = decodedToken.exp * 1000;
    const hasTokenExpired = Date.now() >= expirationTimeMilliseconds ? true : false;
    
    return hasTokenExpired;
}

same problem