I am creating a web application with ReactJS that uses the React Router DOM
library.
This application will contain an authentication system that will be used on some routes, for example:
<Routes>
<Route path="/" element={<Login />} />
<Route path="/dashboard" element={ <PrivateRoute><Dashboard /></PrivateRoute>} />
<Route path="/profile" element={ <PrivateRoute><Profile /></PrivateRoute>} />
</Routes>
My <PrivateRoute>
component have the following code:
import { useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { AuthContext } from '../contexts/authContext';
export default function PrivateRoute({ children }) {
const { accessToken, loading, refreshUser, verifyToken } = useContext(AuthContext);
const navigate = useNavigate();
const doRefresh = async () => {
if(await refreshUser()){
return children;
}
navigate('/');
return;
}
if(loading){
return <div></div>;
}
if(!accessToken){
doRefresh();
}
if(accessToken){
const verify = async () => {
if(await verifyToken()){
return children;
}
doRefresh();
}
verify();
}
return children;
}
The logic of this code was supposed to work perfectly.
When my user opens a private route, the component runs and checks if the accessToken
is null
(false
). If so, it executes the doRefresh()
function, which is responsible for generating a new accessToken
.
If the accessToken
returns true
(have a data) I have to check if the token is still valid, if so, open the children
, if not try to generate a new accessToken
.
The problem with this code is that after the user logs in, the <PrivateRoute>
component is rendered every time along with the continuous execution of the verify
function.
Why is this happening, and how to resolve it?
2
Answers
The issue you’re experiencing is due to the asynchronous nature of your authentication checks and the way React components render. Specifically, the verify and doRefresh functions are being called during the render phase, which causes continuous re-renders and infinite loops.
To resolve this, you should handle side effects (like
asynchronous authentication checks
) within auseEffect
hook. Here’s how you can refactor your PrivateRoute component:useEffect
to perform side effects.state
to manage the authentication status.Here’s the updated code:
Explanation:
Added
authChecked
state to track whether the authentication check hasbeen completed.
The
useEffect
hook runs after the initial render and wheneveraccessToken, navigate, refreshUser, or verifyToken changes. The
checkAuth
function performs the asynchronous authentication checks.If accessToken is not present, it attempts to refresh the user. If
the refresh fails, it navigates to the login page. If accessToken is
present, it verifies the token. If the token is invalid, it attempts
to refresh the user. If the refresh fails, it navigates to the login
page. After successful authentication, it sets authChecked to true.
If loading is true or the authentication check (authChecked) is not
complete, it renders a loading message. Once the authentication check
is complete and successful, it renders the children.
This approach ensures that the asynchronous authentication logic does not cause re-renders or infinite loops by properly managing side effects with useEffect.
You should move asynchronous calls inside of a useEffect hook to avoid the case where your component is stuck in an infinite loop. I have modified your code slightly by introducing the effect hook: