Search code examples
javascriptreactjsreact-hookscomponentsreact-router-dom

Better way to handle 404 page not found in a React app


I'm working on a React app and i'm trying to figure out a better way to redirect to 404 page not found when user hits a wrong path.
Basically my application has a navbar with three main routes (Home, Content, Permissions) and a default redirect to render a route that displays 404 page not found:

<Switch>
  <Route path="/home" component={Home} />
  <Route path="/content" component={Content} />
  <Route path="/permissions" component={Permissions} />
  <Route path="/not-found" component={PageNotFound} />
  <Redirect to="/not-found" />
</Switch>

My problem is with the /Permissions route because this one has many subroutes to display multiple configuration pages and i need to fetchData with a hook, so if the user goes to a wrong path it needs to wait for data fetching before being redirected to page not found:

const Permissions = () => {
  const {isFetchingData} = usePermissionsData();

  if(isFetchingData) {
    return <Loading />;
  } 

  return (
    <div className={styles.permissions} >
      <div className={styles.leftMenu} >
        <LeftMenu />
      </div>
      <div className={styles.content} >
        <Switch>
          <Route path="/permissions" component={PermissionsWelcome}
          <Route path="/permissions/users" component={UsersPermissions}
          <Route path="/permissions/content" component={ContentPermissions}
          <Route path="/permissions/dashboard" component={DashboardPermissions}
          <Redirect to="/not-found" />
        </Switch>
      </div>
    </div>
  );
}

So my question is if it exists a better way to redirect the user to a page not found without needing to wait for the data fetching? (something that i consider is a waste of proccesing).
Any suggestion is welcome!


Solution

  • "A better way" can be subjective, but to help with the objective issue of needing to match a route before checking permissions then I suggest inverting control. By this I mean you should create a custom Route component that will be matched first then check the permissions. It's not clear if your code snippet is a simplified version, but I assume the hook probably also determines if a user has permission to access the current route/resource. If there is an additional check for this then you can conditionally render a Route with the current props or you can render a Redirect to "bounce" the user off the route since they are missing required permission(s).

    Example:

    const PermissionRoute = props => {
      const {isFetchingData} = usePermissionsData();
    
      if(isFetchingData) {
        return <Loading />;
      }
    
      return <Route {...props} />;
    };
    

    Permissions

    const Permissions = () => {
      return (
        <div className={styles.permissions} >
          <div className={styles.leftMenu} >
            <LeftMenu />
          </div>
          <div className={styles.content} >
            <Switch>
              <PermissionRoute path="/permissions/users" component={UsersPermissions} />
              <PermissionRoute path="/permissions/content" component={ContentPermissions} />
              <PermissionRoute path="/permissions/dashboard" component={DashboardPermissions} />
              <PermissionRoute path="/permissions" component={PermissionsWelcome} />
              <Redirect to="/not-found" />
            </Switch>
          </div>
        </div>
      );
    }
    

    Main Routing

    <Switch>
      <Route path="/home" component={Home} />
      <Route path="/content" component={Content} />
      <Route path="/permissions" component={Permissions} />
      <Route path="/not-found" component={PageNotFound} />
      <Redirect to="/not-found" />
    </Switch>