react-router-dom path for main pages and side pages

Viewed 48

I want to display main pages and side pages. In my case, I want to separate pages with one template and pages with another template. At the moment I have a MainPages component with a header and sidebar and AccountVerificationPages without a header and sidebar.

const MainPages = () => {
  // path is "/"
  const { path } = useRouteMatch();
  return (
    <div className={classes.appRoot}>
      <Sidebar />
      <div className={classes.appContent}>
        <Header />
        <Switch>
          <Route path={path} exact>
            <Suspense fallback={<PageLoading />}>
              <IndentedTemplate>
                <MainPage />
              </IndentedTemplate>
            </Suspense>
          </Route>
          <Route path={`${path}browse`} exact>
            <Suspense fallback={<PageLoading />}>
              <IndentedTemplate>
                <MainPage />
              </IndentedTemplate>
            </Suspense>
          </Route>
          <Route path="*">
            <div>404</div>
          </Route>
        </Switch>
      </div>
    </div>
  );
};
const AccountVerificationPages = () => {
  const { path } = useRouteMatch();
  return (
    <Switch>
      <Route path={`${path}/reset-password/:token`} exact>
        <Suspense fallback={<PageLoading />}>
          <FullHeightTemplate>
            <ResetPasswordPage />
          </FullHeightTemplate>
        </Suspense>
      </Route>
      <Route path={`${path}/verify/:token`} exact>
        <Suspense fallback={<PageLoading />}>
          <FullHeightTemplate>
            <VerifyEmailPage />
          </FullHeightTemplate>
        </Suspense>
      </Route>
      <Route path="*">
        <div>404</div>
      </Route>
    </Switch>
  );
};
<Switch>
  {/* Main pages ("/", "/home", "/profile", etc.). keeps the main template with sidebar and header */}
  <Route path="/" component={MainPages} />
  {/* Side pages keeps other template without sidebar and header */}
  <Route path="/account" component={AccountVerificationPages} />
  <Route path="*">
    <div>404</div>
  </Route>
</Switch>

But in this case, I get the routing for the Main Pages component. How to separate routes for MainPages and for AccountVerificationPages?

1 Answers

Issue

It looks like the issue here is an issue with the path order and specificity in the Switch component used to render either MainPages or AccountVerificationPages. Recall that within the Switch component that the first child Route or Redirect that matches is rendered.

In your code's case, the path="/" path will always match and render the MainPages component.

<Switch>
  <Route path="/" component={MainPages} /> // <-- Always matched and rendered
  <Route path="/account" component={AccountVerificationPages} />
  <Route path="*">
    <div>404</div>
  </Route>
</Switch>

Solution

Within the Switch path order and specificity matters. You should order the routes in inverse order of path specificity. This also removes the need for the exact prop in almost all cases.

Invert the paths for MainPages and AccountVerificationPages components. "/account" is more specific than "/" and should tried to be matched first. You'll need to also remove the "*" wildcard route since it can't be matched because "/" will always match and render. This is ok though because both MainPages and AccountVerificationPages both already handle unhandled route paths.

<Switch>
  <Route path="/account" component={AccountVerificationPages} />
  <Route path="/" component={MainPages} />
</Switch>

...

const MainPages = () => {
  // path is "/"
  const { path } = useRouteMatch();
  return (
    <div className={classes.appRoot}>
      <Sidebar />
      <div className={classes.appContent}>
        <Header />
        <Switch>
          <Route path={`${path}browse`}>
            <Suspense fallback={<PageLoading />}>
              <IndentedTemplate>
                <MainPage />
              </IndentedTemplate>
            </Suspense>
          </Route>
          <Route path={path}>
            <Suspense fallback={<PageLoading />}>
              <IndentedTemplate>
                <MainPage />
              </IndentedTemplate>
            </Suspense>
          </Route>
          <Route path="*">
            <div>404</div>
          </Route>
        </Switch>
      </div>
    </div>
  );
};

const AccountVerificationPages = () => {
  const { path } = useRouteMatch();
  return (
    <Switch>
      <Route path={`${path}/reset-password/:token`}>
        <Suspense fallback={<PageLoading />}>
          <FullHeightTemplate>
            <ResetPasswordPage />
          </FullHeightTemplate>
        </Suspense>
      </Route>
      <Route path={`${path}/verify/:token`}>
        <Suspense fallback={<PageLoading />}>
          <FullHeightTemplate>
            <VerifyEmailPage />
          </FullHeightTemplate>
        </Suspense>
      </Route>
      <Route path="*">
        <div>404</div>
      </Route>
    </Switch>
  );
};
Related