一个React Router教程

134 阅读3分钟

一个React Router教程,教你如何使用React Router 6的Descendant Routes。这个React Router v6教程的代码可以在这里找到。

嵌套路由的前一个教程已经向你展示了如何在Outlet组件的帮助下,根据URL替换组件的一部分。这样,在最后一个例子中,我们在Users组件中渲染了一个User组件。当Users组件列出所有的用户时,User组件显示了匹配用户的详细信息。

然而,有时你并不想嵌套这些路由,而是想在它们之间引入一个开关。例如,与前面的例子相反,你可能想显示一个用户列表(如UserList组件)或一个用户的详细信息(如UserItem组件),但绝不是在同一路由上同时显示两者。然而,这两个路由应该在总的/users 路由下运行。实现这一目标的直接方法是将两者作为独立的路由添加到App组件中。

const App = () => {
  const users = [
    { id: '1', firstName: 'Robin', lastName: 'Wieruch' },
    { id: '2', firstName: 'Sarah', lastName: 'Finnley' },
  ];

  return (
    <>
      <h1>React Router</h1>

      <nav>
        <Link to="/home">Home</Link>
        <Link to="/users">Users</Link>
      </nav>

      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="users" element={<UserList users={users} />} />
          <Route path="users/:userId" element={<UserItem  />} />
          <Route path="*" element={<NoMatch />} />
        </Route>
      </Routes>
    </>
  );
};

为了完整起见,这些可以是UserList和UserItem组件,它们被用来在两个组件之间来回链接。

const UserList = ({ users }) => {
  return (
    <>
      <h2>Users</h2>
      <h3>User List</h3>

      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={user.id}>
              {user.firstName} {user.lastName}
            </Link>
          </li>
        ))}
      </ul>
    </>
  );
};

const UserItem = () => {
  const { userId } = useParams();

  return (
    <>
      <h2>Users</h2>
      <h3>User Item: {userId}</h3>

      <Link to="/users">Back to Users</Link>
    </>
  );
};

这样就可以了。在UserList和UserItem组件之间有一个切换。只有其中一个显示出来,而不是像我们之前在嵌套路由的例子中那样同时显示。然而,仅仅通过观察App组件及其定义的Route组件,你可能会很感兴趣,为这两个/users 路线寻找一个更优雅的解决方案。

这两个路由共享一个特定的域(这里是:用户),也许不应该以这种方式显示在顶级的App组件中,作为最佳实践,App组件应该只定义顶级的路由(例如:/users ,而不是/users/:userId )。还有一个更优雅的解决方案,叫做子嗣路由

下级路由是指不在顶层路由组件集合中定义的路由,而是在组件树的某个地方定义的。我们将通过将之前的App组件改编成下面的版本来探索这个概念。

const App = () => {
  const users = [
    { id: '1', firstName: 'Robin', lastName: 'Wieruch' },
    { id: '2', firstName: 'Sarah', lastName: 'Finnley' },
  ];

  return (
    <>
      <h1>React Router</h1>

      <nav>
        <Link to="/home">Home</Link>
        <Link to="/users">Users</Link>
      </nav>

      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="users/*" element={<Users users={users} />} />
          <Route path="*" element={<NoMatch />} />
        </Route>
      </Routes>
    </>
  );
};

正如你所看到的,现在只有一个顶层的用户相关路由。新的东西是尾部的星号 (*),它表示这个路由和它的组件(这里是:Users组件)渲染了下级路由。让我们来看看新的Users组件:

const Users = ({ users }) => {
  return (
    <>
      <h2>Users</h2>

      <Routes>
        <Route index element={<UserList users={users} />} />
        <Route path=":userId" element={<UserItem users={users} />} />
      </Routes>

      <Outlet />
    </>
  );
};

Users组件是用户域的一个伞状组件,它渲染了两个子嗣路由和一个出口组件。其中一个后裔路由是一个索引路由(只匹配/users ),而另一个路由是一个动态路由(匹配/users/:userId )。根据匹配的路由,UserList或UserItem组件会在Outlet中呈现。

毕竟,通过使用子嗣路由,你获得了将路由分组到更细粒度的域中的能力。此外,如果不是在顶层的App组件中,你还可以灵活地定义你的Routes组件集。正如你在这个例子中所看到的,子嗣Routes可以很好地用于在一个特定域中的多个路由之间切换。


如果你碰巧在你的React应用中使用了React Router,Nested Routes可以极大地提升你的用户体验,让你的用户访问你的应用中非常具体的部分,同时将这些部分作为URL共享。此外,Descendant Routes不仅可以用来定义顶级React组件中的路由,还可以深入嵌套在你的React组件的层次结构中,同时能够将它们归入特定的域。