preactpreact-router

How to protect routes with preact-router?


I'm able to use "preact-router" on a component level together with a authContext.

Eg:

export const CartPage = (): JSX.Element => {
  const { isAuthenticated } = useAuthContext();

  return (
    <>
      {isAuthenticated ? (
        <Cart />
      ) : (
        (() => {
          route("/login");
        })()
      )}
    </>
  );
};

This works great when using the app as intended by navigating via the apps navigation, views etc. But if I try to navigate by using the browsers address bar to hit a protected route eg: "/cart" the redirect via route() to login is not triggered, instead I just get a blank page...

app.tsx:

export function App() {
  return (
    <div>
      <AuthProvider>
        <Navbar />

        <Layout>
          <ViewToggleProvider>
            <Router>
              <Route path="/" component={HomePage} />

              <AsyncRoute path="/login" component={LoginPage} />

              <AsyncRoute exact path="/cart" component={CartPage} />

              <AsyncRoute
                path="/orders/:orderId"
                component={OrderDetailsPage}
              />
            </Router>
          </ViewToggleProvider>
        </Layout>
      </AuthProvider>
    </div>
  );
}

Perhaps I need to protect the routes on a "route" level over a "component" level?

Glad for any assistance here.


Solution

  • Easiest way would be to make a ProtectedRoute component, rather than handling that in each component:

    function ProtectedRoute(props) {
        const { isAuthenticated } = useAuthContext();
    
        useEffect(() => {
            if (!isAuthenticated) route('/login', true);
        }, [isAuthenticated]);
    
        return <Route {...props} />;
    }
    

    Then to use it:

    <Router>
      <ProtectedRoute exact path="/cart" component={CartPage} />
    </Router>
    

    You need to use useEffect (or similar) to ensure it's triggered on browser navigation.