Home > Software design >  How to make a Not Found page with complex dymanic nested routes in React Router 6?
How to make a Not Found page with complex dymanic nested routes in React Router 6?

Time:08-14

How to make a Not Found page with complex dynamic nested routes in React Router 6?

For example I have dynamic routes for categories and product pages. It looks like this:

site.com/jeans
site.com/jeans/qazXzx123122

To render the data on category and product pages I use the same approach:

  • Get URL from useParams
  • Find category and products with the same URL in Redux-state
  • Render data on a page

CategoryPage

const { products } = useSelector((state: RootState) => state.product);
const { categories } = useSelector((state: RootState) => state.category);
const { url } = useParams();
const categoryProducts = products.filter((product) => product.category.url === url);
const category = categories.find((category) => category.url === url) as Category;

If I go to wrong URL e.g. to site.com/jeans111 then app crashes because there's no category with that URL and category === undefined. I tried to solved the problem with useEffect hook and redirect but no success.

Not working approach

useEffect(() => {
  if (!category.name) {
    return navigate('/', {
      replace: true,
    });
  }
}, [category, navigate]);

Routes

const routes = useRoutes([
  {
    path: PATHS.showcase,
    element: <ShowcasePage />,
    children: [
      {
        path: '/',
        element: isDataLoaded ? <DiscountProductsPage /> : <Loader />,
      },
      {
        path: ':url',
        children: [
          {
            index: true,
            element: isDataLoaded ? <CategoryPage /> : <Loader />,
          },
          {
            path: ':id',
            element: isDataLoaded ? <ProductPage /> : <Loader />,
          },
          {
            path: '*',
            element: <NotFound />
          }
        ],
      },
      { path: PATHS.wishlist, element: isDataLoaded ? <WishlistPage /> : <Loader /> },
      {
        path: PATHS.cart,
        element: isDataLoaded ? <CartPage /> : <Loader />,
      },
      {
        path: `${PATHS.cart}/${PATHS.success}`,
        element: <CheckoutSuccessPage />,
      },
    ],
  },
  {
    path: PATHS.admin,
    element: <AdminPage />,
    children: [
      {
        path: PATHS.orders,
        element: <OrdersPage />,
      },
      {
        path: PATHS.products,
        element: <ProductsPage />,
      },
      {
        path: PATHS.settings,
        element: <SettingsPage />,
      },
    ],
  },
]);

CodePudding user response:

I think your approach is correct, but you want to redirect off the route when there is no category to render content for.

Example:

const navigate = useNavigate();
const { url } = useParams();
...
const category = categories.find((category) => category.url === url) as Category;

useEffect(() => {
  if (!category) {
    navigate('/', { replace: true });
  }
}, [category, navigate]);

Though it may be better to display a fallback screen instead of just bouncing the user off the route.

const navigate = useNavigate();
const { url } = useParams();
...
const category = categories.find((category) => category.url === url) as Category;

...

return category
  ? (
    ...JSX...
  )
  : (
    <div>No match found.</div>
  );
  • Related