7

I am trying to implement a protected route to only allow logged in users to access my app. The code I have written seems to work, I am redirected to my login page when I try to access the homepage without being logged in, however once I am logged in I can access the page but I does not render and I get the following error: Click here for error

I have tried multiple methods and wrapping the element in my protected route seems like the V6 way of doing things, however it doesn't seem to work for me:

My protected route

interface PrivateRouteProps extends RouteProps {
}

const PrivateRoute: React.FC<PrivateRouteProps> = ({...rest}) => {
    const auth = useAuth();

    if (auth?.user === null) {
        return <Navigate to='/'/>;
    }
    return <Route {...rest}/>;
};

export default PrivateRoute;

My app (route usage)

function App() {
    useEffect(() => {
      API
        .get('api', '/reservation', {})
        .then(response => {
          console.log(response);
        })
        .catch(error => {
          console.log(error.response);
        });

    }, [])
    return (
      <Router>
        <Routes>
          <Route path='/' element={<LoginPage />}/>
          <Route path='/consultAndReserve' element={<PrivateRoute><Navbar/><ConsultReserve/></PrivateRoute>} />
          <Route path='/consultAndReserve/:date' element={<><Navbar/><ConsultReserveWithDate/></>}/>
          <Route path='/myReservations'  element={<><Navbar/><Reservations/></>}/>
          <Route path='/tracing'  element={<><Navbar/><Tracing/></>}/>
        </Routes>
      </Router>
  );
}

What am I doing wrong?

ginoboy
  • 83
  • 2
  • 6

1 Answers1

9

It's fairly trivial to create a "PrivateRoute" component, even in TypeScript.

In your case you can't directly render a Route component as the error points out. This was a breaking change between RRDv5 and RRDv6. You can render the children prop since you are directly wrapping the components you want to protect.

Example:

const PrivateWrapper = ({ children }: { children: JSX.Element }) => {
  const auth = useAuth();
  return auth?.user ? children : <Navigate to="/" replace />;
};

Usage:

<Routes>
  ...
  <Route
    path="/consoleAndReserve"
    element={(
      <PrivateWrapper>
        <Navbar />
        <ConsultReserve />
      </PrivateWrapper>
    )}
  />
  ...
</Routes>
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • I think I understand what my issue is. The problem is that I am trying to render a Route with my PrivateRoute, which I am not allowed to do. With your method, I would only redirect the user to said page using Navigate instead of a Route? I tried applying your code to mine and I get the following line: JSX element type 'PrivateWrapper' does not have any construct or call signatures. – ginoboy Mar 15 '22 at 20:34
  • @ginoboy Ok, so this is where my minimal experience with Typescript shows through. Let me play around to see if I can get a correct typing for this. – Drew Reese Mar 15 '22 at 20:51
  • 1
    @ginoboy `React.FC` is very general, but works for a general auth wrapper. – Drew Reese Mar 15 '22 at 21:04
  • 4
    `const PrivateWrapper = ({ children }: { children: JSX.Element }) => {` should do it. That requires that `children` is a `JSX.Element` (more specific than `ReactNode`). You need that type in order to `return children` directly because a component can only return `JSX.Element | null`. – Linda Paiste Mar 15 '22 at 21:22
  • @LindaPaiste Ah, ok, so the initial answer was *close*. Thanks! – Drew Reese Mar 15 '22 at 21:24
  • 1
    Drew's revised answer did the trick for me. What is the advantage of using JSX.Element over ReactNode? – ginoboy Mar 16 '22 at 02:55
  • @ginoboy This [SO post](https://stackoverflow.com/questions/58123398/when-to-use-jsx-element-vs-reactnode-vs-reactelement) cleared a bit of it up for me. – Drew Reese Mar 16 '22 at 04:30