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>
);