You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 10:54:14