I have this React Component for Privates routes. I want to check if user is logged in, so I check if jwt is in local storage and if token is not expired. If this doesn't happen I want to log user out, since token is expired.
I have a react warning that says I can't return a function and Component at the same level. What would be the best approach to achieve this?
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import dayjs from 'dayjs';
import { useUserStore } from '@store/userStore';
import Main from '@layouts/Main';
function PrivateRoutes() {
const { expiresIn, removeUser } = useUserStore();
const now = dayjs();
const handleRemoveUser = () => {
removeUser();
};
return window.localStorage.getItem('jwt') && dayjs(expiresIn).isAfter(now) ? (
<Main>
<Outlet />
</Main>
) : (
<>
{handleRemoveUser}
<Navigate to='/login' />
</>
);
}
export default PrivateRoutes;
I have tried using handler functions, arrow functions but not sure how to approach this situation
CodePudding user response:
The render function of a React component is to be considered a pure function, there should be no unintentional side-effects during the render such as calling functions to de-authenticate users. For this you'll need to use the useEffect
hook to issue an intentional side-effect as part of the normal React component lifecycle.
Compute the authentication condition and use as a dependency for a useEffect
hook that conditionally calls removeUser
, while at the same time conditionally returning valid JSX in the function return.
Example:
function PrivateRoutes() {
const { expiresIn, removeUser } = useUserStore();
const now = dayjs();
const jwt = window.localStorage.getItem('jwt');
const isNotExpired = dayjs(expiresIn).isAfter(now);
const isAuth = jwt && isNotExpired;
React.useEffect(() => {
if (!isAuth) {
removeUser();
}
}, [isAuth, removeUser]);
return isAuth
? (
<Main>
<Outlet />
</Main>
)
: <Navigate to="/login" replace />;
}
CodePudding user response:
There is another way of doing that by Using HOC
const AuthCheck = (WrappedComponent: ComponentType) =>
function decorator(props) {
const [mount, setMount] = useState(false);
const { expiresIn, removeUser } = useUserStore();
const now = dayjs();
const jwt = window.localStorage.getItem('jwt');
const isNotExpired = dayjs(expiresIn).isAfter(now);
const isAuth = jwt && isNotExpired;
useEffect(() => {
setMount(true);
}, []);
useEffect(() => {
if (!isAuth) {
removeUser();
}
}, [isAuth, removeUser]);
return mount ? (
isAuth ? (
<WrappedComponent {...props} />
) : (
<>
Unathorised login <Navigate to='/login' replace />
</>
)
) : null;
};
export default AuthCheck;
need wrapped protected main container component with AuthCheck
HOC
eg:
const Dashboard = () => {
return <></>
}
export default AuthCheck(Dashboard);