React Router 6复杂动态嵌套路由下404页面的实现与崩溃修复
解决React Router 6动态嵌套路由的404页面问题
首先先解决你代码里直接崩溃的核心问题:你现在强制把categories.find(...)断言成Category,但找不到分类时这个值是undefined,后续访问category.name直接触发报错,这就是之前useEffect方案无效的原因——代码还没走到useEffect就崩了。
第一步:先修复组件崩溃问题
修改CategoryPage的代码,先处理分类不存在的情况,避免强制断言导致的崩溃:
const { products } = useSelector((state: RootState) => state.product); const { categories } = useSelector((state: RootState) => state.category); const { url } = useParams(); const category = categories.find((category) => category.url === url); // 去掉强制类型断言 const categoryProducts = category ? products.filter((product) => product.category.url === url) : [];
这样即使找不到分类,category为undefined也不会直接崩溃,接下来再处理404或重定向逻辑。
方案一:组件内直接判断渲染404
修复崩溃后,可以在组件内直接判断分类是否存在,不存在则渲染NotFound组件:
import { NotFound } from './path/to/NotFound'; const CategoryPage = () => { const { products } = useSelector((state: RootState) => state.product); const { categories } = useSelector((state: RootState) => state.category); const { url } = useParams(); const category = categories.find((category) => category.url === url); const categoryProducts = category ? products.filter((product) => product.category.url === url) : []; // 找不到分类时直接渲染404 if (!category) { return <NotFound />; } // 正常渲染分类页面内容 return ( // 你的分类页面JSX结构 ); };
如果偏好重定向到首页,也可以用useNavigate实现,注意用useEffect避免无限循环:
const navigate = useNavigate(); useEffect(() => { if (!category) { navigate('/', { replace: true }); } }, [category, navigate]); // 未找到分类时返回null避免渲染多余内容 if (!category) { return null; }
方案二:路由层面配置全局404+路由守卫
你当前的path: '*'放在:url的子路由里,但:url会匹配任何非空字符串(比如jeans111),导致这个*路由永远不会触发。需要调整路由结构,把全局404放在最外层,同时给动态路由加守卫提前校验分类。
1. 编写分类路由守卫组件
import { Outlet, Navigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; const CategoryGuard = () => { const { categories } = useSelector((state: RootState) => state.category); const { url } = useParams(); const categoryExists = categories.some((category) => category.url === url); if (!categoryExists) { return <Navigate to="/404" replace />; } // 分类存在则渲染子路由(CategoryPage或ProductPage) return <Outlet />; };
2. 修改路由配置
把全局404路由加到最外层,同时用守卫包裹:url的子路由:
const routes = useRoutes([ { path: PATHS.showcase, element: <ShowcasePage />, children: [ { path: '/', element: isDataLoaded ? <DiscountProductsPage /> : <Loader />, }, { path: ':url', element: isDataLoaded ? <CategoryGuard /> : <Loader />, // 用守卫替代直接渲染 children: [ { index: true, element: <CategoryPage /> }, { path: ':id', element: <ProductPage /> }, ], }, { 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 /> }, ], }, // 全局404路由,放在最后匹配所有未定义路径 { path: '/404', element: <NotFound /> }, { path: '*', element: <Navigate to="/404" replace /> }, ]);
这样不管是访问不存在的分类,还是其他未定义的路由,都会跳转到404页面。
方案三:使用React Router 6.4+的Loader功能(推荐)
如果用的是React Router 6.4+的Data Router,可以用loader提前校验分类是否存在,不存在则抛出404:
// 定义分类校验loader const categoryLoader = ({ params }) => { const categories = store.getState().category.categories; const category = categories.find(c => c.url === params.url); if (!category) { throw new Response('', { status: 404, statusText: 'Not Found' }); } return { category }; }; // 路由配置中添加loader和errorElement const routes = useRoutes([ { path: PATHS.showcase, element: <ShowcasePage />, children: [ // ...其他路由 { path: ':url', loader: categoryLoader, errorElement: <NotFound />, // 校验失败时渲染404 children: [ { index: true, element: <CategoryPage /> }, { path: ':id', element: <ProductPage /> }, ], }, // ...其他路由 ], }, // ...其他路由 ]);
这种方式会在进入路由前完成校验,代码更简洁,也符合Data Router的最佳实践。
内容的提问来源于stack exchange,提问作者qweezz
相关产品推荐
相关产品推荐

