如何在 react-router v6 的树结构中创建深层路径

how to create deep paths in a tree structure in react-router v6

这里有一个 codesandbox 可以说明问题:

如果我有一棵这样的对象树:

const tasks: Task[] = [
  {
    id: 1,
    name: "one",
    children: [
      {
        id: 3,
        name: "three",
        children: [{ id: 5, name: "five", children: [] }]
      }
    ]
  },
  {
    id: 2,
    name: "two",
    children: [
      { id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
    ]
  }
];

我希望能够 link 变成 url 这样的:

/tasks/2/children/4/children/6.

到目前为止,我只能通过以下路线结构将其到达 link 到 tasks/2/children/6

  let routes: RouteObject[] = [
    {
      path: "tasks",
      element: <Layout />,
      children: [
        {
          path: ":id",
          element: <TaskPage />,
          children: [
            { path: "children/:id", element: <TaskPage />, index: true }
          ]
        },
        {
          index: true,
          element: <TaskTreePage />
        }
      ]
    },
    {
      index: true,
      element: <Navigate to="tasks" />
    }
  ];

  let element = useRoutes(routes);

每次进入新的深度时,我都需要不断添加嵌套路线。

有没有一种方法可以动态构建适用于任何深度的深层嵌套路由路径?

当路由是索引路由时,它不应该也使用 path 属性,因为它理解路径是包装父布局路由的路径。

TaskPage 组件应该是动态的,而不是路由配置。路由配置应该是在 "/tasks/:id/*" 上呈现“根”TaskPage 组件的“根”路由的入口点。 TaskPage 组件现在有效地递归地在它呈现的新嵌套相对路由 <Route path="children/:id/*" element={<TaskPage />} /> 中呈现自身的另一个实例。

代码示例:

应用程序

const routes: RouteObject[] = [
  {
    path: "/tasks",
    element: <Layout />,
    children: [
      {
        path: ":id/*",
        element: <TaskPage />
      },
      {
        index: true,
        element: <TaskTreePage />
      }
    ]
  },
  {
    index: true,
    element: <Navigate to="/tasks" />
  }
];

export default function App() {
  const element = useRoutes(routes);
  return element;
}

任务页面

这就是“魔法”发生的地方。它渲染了一个新的 Routes 组件,其中包含两条路由,一条索引路由用于渲染当前深度的 links 和一条渲染嵌套子级的路由。

function TaskPage() {
  const { id } = useParams();
  const task = findTask(id as string, tasks);

  if (!task) {
    return <h2>Not Found!</h2>;
  }

  return (
    <Routes>
      <Route
        index
        element={
          <div>
            <h2>task of {task.name}</h2>
            <div>
              <Link to="../..">Back</Link>
              <div>
                <h2>Children</h2>
                {task.children.map((child: Task) => (
                  <Link key={child.id} to={`children/${child.id}`}>
                    {child.name}
                  </Link>
                ))}
              </div>
            </div>
          </div>
        }
      />
      <Route path="children/:id/*" element={<TaskPage />} />
    </Routes>
  );
}

注意尾随通配符 "*" 匹配器以允许匹配嵌套路由。 另请注意,后面的 link 现在是 "../..",因为每一层嵌套都会推动两个路径段,因此要“返回”两个路径段需要删除。

const tasks: Task[] = [
  {
    id: 1,
    name: "one",
    children: [
      {
        id: 3,
        name: "three",
        children: [
          {
            id: 5,
            name: "five",
            children: [{ id: 7, name: "seven", children: [] }]
          }
        ]
      }
    ]
  },
  {
    id: 2,
    name: "two",
    children: [
      { id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
    ]
  }
];