Search code examples
javascriptreactjsreact-router-dom

Why a particular page from "React-router-dom" is having 'not-found text message' at its bottom in browser window (react-router-dom version -6)?


Description - I made a Route.tsx file where I would be storing all of my routes, into the app, including the App.tsx (at path: '/') itself.

Here's my Route.tsx file - also (Approach 1)

import { lazy } from "react";
import { createBrowserRouter, Navigate, RouteObject } from "react-router-dom";
import App from "../layout/App";
 
// react - lazy paint
const Home = lazy(() => import("../pages/home/Home"));
const Toys = lazy(() => import("../pages/products/Toys"));
const Books = lazy(() => import("../pages/learning/Learning"));
const NotFound = lazy(() => import("../pages/notFound/NotFound"));
 
export const routes : RouteObject[] = [
  {
    path: '/',
    element: <App />, 
    children: [
      { path: "toys", element: <Toys /> },
      { path: "/", element: <Home /> },
      { path: "books", element: <Books /> },
      { path: "not-found", element: <NotFound /> },
      { path: "*", element: <Navigate replace to = {"/not-found"} /> }
    ]
  }
];
 
// export all routes
export const router = createBrowserRouter(routes);

These all above mentioned routes are meant to wrapped inside of a container (from Material UI). Whilst, there is one page, which I wish not to enclose into the container, named Cars.tsx, so I modified my App.tsx file like the one mentioned below:

App.tsx file

import { Container, CssBaseline, ThemeProvider } from "@mui/material";
import { Fragment, lazy, Suspense } from "react";
import { Outlet, Route, Routes } from "react-router-dom";
import AppLoad from "./AppLoad";
import Header from "./Header";
 
// react - lazy imports
const Cars = lazy(() => import("../pages/cars"));
 
function App() {
  return (
    <Fragment>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <Header />
 
        // for lazy paint load ...
        <Suspense fallback={<AppLoad message={'Please wait. Loading ...'} />}>
          <Routes>
            <Route path="cars" element={<Cars />} />
            // this particular page, I don't want to enclose it into 
            // the container, because it contains a hero image.
          </Routes>
 
          <Container maxWidth={'xl'}>
            <Outlet /> // all the rest of pages are inside of container
          </Container>
        </Suspense>
      </ThemeProvider>
    </Fragment>
  );
}

export default App;

Problem Description - Everything is working fine, only problem I am facing is when I am at "/cars" page, at the very bottom of the page, it is also rendering "Not-Found" (previously it was solved by <Switch /> in react-router-dom's version < 6).

Expected Behavior - I don't want to see "Not Found" message at the very bottom of "Cars" page. How to fix it?

I know, the fix would be very simple, but I couldn't figure it out.

Also, I would like to ask whether or not the above mentioned approach is better (like creating a separate file for Route, and then using it as <Outlet />), or the traditional approach is better where we have as.

Approach 2

<Routes>
  <Route path='page1' element={<Page1 />} />
  <Route path='page2' element={<Page2 />} />
  <Route path='page1' element={<Page3 />} />
</Routes>

Would it be going to make any difference into the First content-ful paint loading of any routed page so far if I work with any of the approach as mentioned above?


Solution

  • The issue it seems is that the App component is rendering both an Outlet for nested routes and a Routes component for the "/cars" descendent route. When the path is "/cars" the App component is rendered and renders the matching "/cars" descendent route is rendered, but there is no matching nested "/cars" route so it looks like the NotFound route/component is additionally rendered in the Outlet.

    If the goal is to not render the "/cars" route and Cars component inside the Container then I'd recommend creating an additional layout route and conditionally render the nested routes into the appropriate layout routes.

    Example:

    import { Container } from "@mui/material";
    import { Outlet } from 'react-router-dom';
    
    const ContainerLayout = () => (
      <Container maxWidth="xl">
        <Outlet />
      </Container>
    );
    

    routes.tsx

    import { lazy } from "react";
    import { createBrowserRouter, Navigate, RouteObject } from "react-router-dom";
    import App from "../layout/App";
     
    // react - lazy paint
    const Home = lazy(() => import("../pages/home/Home"));
    const Toys = lazy(() => import("../pages/products/Toys"));
    const Books = lazy(() => import("../pages/learning/Learning"));
    const NotFound = lazy(() => import("../pages/notFound/NotFound"));
    const Cars = lazy(() => import("../pages/cars"));
     
    export const routes : RouteObject[] = [
      {
        path: '/',
        element: <App />, 
        children: [
          {
            element: <ContainerLayout />,
            children: [
              { index: true, element: <Home /> },
              { path: "toys", element: <Toys /> },
              { path: "books", element: <Books /> },
              { path: "not-found", element: <NotFound /> }
            ],
          },
          { path: "cars" element: <Cars /> },
          { path: "*", element: <Navigate replace to="/not-found" /> }
        ]
      },
    ];
     
    // export all routes
    export const router = createBrowserRouter(routes);
    

    App.tsx

    import { CssBaseline, ThemeProvider } from "@mui/material";
    import { Suspense } from "react";
    import { Outlet } from "react-router-dom";
    import AppLoad from "./AppLoad";
    import Header from "./Header";
    
    function App() {
     
    return (
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <Header />
     
        // for lazy paint load ...
        <Suspense fallback={<AppLoad message="Please wait. Loading ..." />}>
          <Outlet />
        </Suspense>
       </ThemeProvider>
      );
    }
    
    export default App;