Home > front end >  How can I call a function and return a React Component at the same time?
How can I call a function and return a React Component at the same time?

Time:12-21

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);
  • Related