React Router v6 官方文档翻译 (五) ---- 路由组件

专栏说明:对于react-router v6版本,有同事反应缺少相对完整的中文文档,查看不方便。为了便于使用,作者对官网文档进行针对性翻译,便于v6版本更广泛的被使用。

类型定义:

declare function Link(props: LinkProps): React.ReactElement;

interface LinkProps
  extends Omit<
    React.AnchorHTMLAttributes<HTMLAnchorElement>,
    "href"
  > {
  replace?: boolean;
  state?: any;
  to: To;
  reloadDocument?: boolean;
}

type To = Partial<Location> | string;

内置元素 <Link> 是用于通过点击跳转页面的。 在 react-router-dom中,<Link> 会渲染一个<a> 标签, 标签的 href 为Link的 to 属性. 也就是说,右键点击一个 <Link>也是起作用的. 你也可以这么写:<Link reloadDocument> , 以此来阻止浏览器默认事件。

使用案例:

import * as React from "react";
import { Link } from "react-router-dom";

function UsersIndexPage({ users }) {
  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={user.id}>{user.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

<Link to> 的 to 参数,如果不是以 / 开头的话,表示是一个相对路由。路径中可能会包含 ..来检索上一级,类似目录指令的 cd

在 React Native 中使用:

类型定义:

declare function Link(props: LinkProps): React.ReactElement;

interface LinkProps extends TouchableHighlightProps {
  children?: React.ReactNode;
  onPress?(event: GestureResponderEvent): void;
  replace?: boolean;
  state?: any;
  to: To;
}

<Link> 用于响应用户点击后跳转其他的 View, 渲染为一个 <a> 标签。 在 react-router-native, 中, <Link> 渲染一个 TouchableHighlight。可以使用TouchableHighlight配置项来自定义样式和行为。

import * as React from "react";
import { View, Text } from "react-native";
import { Link } from "react-router-native";

function Home() {
  return (
    <View>
      <Text>Welcome!</Text>
      <Link to="/profile">Visit your profile</Link>
    </View>
  );
}

类型定义:

declare function NavLink(
  props: NavLinkProps
): React.ReactElement;

interface NavLinkProps
  extends Omit<
    LinkProps,
    "className" | "style" | "children"
  > {
  caseSensitive?: boolean;
  children?:
    | React.ReactNode
    | ((props: { isActive: boolean }) => React.ReactNode);
  className?:
    | string
    | ((props: {
        isActive: boolean;
      }) => string | undefined);
  end?: boolean;
  style?:
    | React.CSSProperties
    | ((props: {
        isActive: boolean;
      }) => React.CSSProperties);
}

<NavLink> 是特殊类型的<Link>, 可以记录 "active" 状态。 可用于面包屑或者菜单的选中点亮。对于用户和读者来说也提供了上下文的导航说明。

默认的, <NavLink>激活时,会加上类名active,v5中也是这么做的. 不同的是,新版本v6.0.0-beta.3 中属性移除了 activeClassName 和 activeStyle,取而代之的是 style 或者 className 属性,用于自定义行内样式或者自定义类名,当然也可以穿一个函数作为属性给 <NavLink> 组件来自定义行为。

例子:

import * as React from "react";
import { NavLink } from "react-router-dom";

function NavList() {
  // This styling will be applied to a <NavLink> when the
  // route that it links to is currently selected.
  let activeStyle = {
    textDecoration: "underline",
  };

  let activeClassName = "underline";

  return (
    <nav>
      <ul>
        <li>
          <NavLink
            to="messages"
            style={({ isActive }) =>
              isActive ? activeStyle : undefined
            }
          >
            Messages
          </NavLink>
        </li>
        <li>
          <NavLink
            to="tasks"
            className={({ isActive }) =>
              isActive ? activeClassName : undefined
            }
          >
            Tasks
          </NavLink>
        </li>
        <li>
          <NavLink to="tasks">
            {({ isActive }) => (
              <span
                className={
                  isActive ? activeClassName : undefined
                }
              >
                Tasks
              </span>
            )}
          </NavLink>
        </li>
      </ul>
    </nav>
  );
}

如果你很熟悉 v5 的 API,可以二次封装一下使用:

import * as React from "react";
import { NavLink as BaseNavLink } from "react-router-dom";

const NavLink = React.forwardRef(
  ({ activeClassName, activeStyle, ...props }, ref) => {
    return (
      <BaseNavLink
        ref={ref}
        {...props}
        className={({ isActive }) =>
          [
            props.className,
            isActive ? activeClassName : null,
          ]
            .filter(Boolean)
            .join(" ")
        }
        style={({ isActive }) => ({
          ...props.style,
          ...(isActive ? activeStyle : null),
        })}
      />
    );
  }
);

end 属性用于控制不会级联点亮。如果想要只在完全匹配路由是active,而不是在该路由上几级路径都被active,可以使用这个属性。下面的例子,只会在路径为 / 时匹配 active,其他任何路径都不会点亮:

<NavLink to="/" end>
  Home
</NavLink>

3. <Navigate>

类型定义:

declare function Navigate(props: NavigateProps): null;

interface NavigateProps {
  to: To;
  replace?: boolean;
  state?: any;
}

<Navigate> 被渲染时,会立即改变当前的location. 和hooks useNavigate 可以达到一样的效果,传递的参数也一样。

提示:

<Navigate>可以在不能使用路由 hooks 的地方代替 useNavigate

下面的例子中,会先拿到登录状态的user,若没有user,渲染登录表单;如果拿到了登录信息,则通过<Navigate>直接跳转到 dashboard 页面:

import * as React from "react";
import { Navigate } from "react-router-dom";

class LoginForm extends React.Component {
  state = { user: null, error: null };

  async handleSubmit(event) {
    event.preventDefault();
    try {
      let user = await login(event.target);
      this.setState({ user });
    } catch (error) {
      this.setState({ error });
    }
  }

  render() {
    let { user, error } = this.state;
    return (
      <div>
        {error && <p>{error.message}</p>}
        {user && (
          <Navigate to="/dashboard" replace={true} />
        )}
        <form
          onSubmit={(event) => this.handleSubmit(event)}
        >
          <input type="text" name="username" />
          <input type="password" name="password" />
        </form>
      </div>
    );
  }
}

4. <Outlet>

类型定义:

interface OutletProps {
  context?: unknown;
}
declare function Outlet(
  props: OutletProps
): React.ReactElement | null;

<Outlet> 可用于父路由中,用来渲染子路由组件. 这需要开发者合理安排嵌套 UI 的布局. 当直接访问父路由时,会自动渲染父路由中配置了 index 属性的子路由来替代 Outlet, 如果没有 index 路由,该位置不渲染。

下面的例子中,访问 / 会渲染 Dashboard 组件, Outlet留白; 访问 /tasks 则会渲染 Dashboard,Outlet位置会渲染 DashboardTasks:

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      <Outlet />
    </div>
  );
}

function App() {
  return (
    <Routes>
      <Route path="/" element={<Dashboard />}>
        <Route
          path="messages"
          element={<DashboardMessages />}
        />
        <Route path="tasks" element={<DashboardTasks />} />
      </Route>
    </Routes>
  );
}

当然了,父子组件仍然可以传参,使用 context 和 useOutletContext 组合拳即可,详情可参考本专栏的 hook 专题。

5. <Routes> 与 <Route>

类型声明:

declare function Routes(
  props: RoutesProps
): React.ReactElement | null;

interface RoutesProps {
  children?: React.ReactNode;
  location?: Partial<Location> | string;
}

declare function Route(
  props: RouteProps
): React.ReactElement | null;

interface RouteProps {
  caseSensitive?: boolean;
  children?: React.ReactNode;
  element?: React.ReactNode | null;
  index?: boolean;
  path?: string;
}

<Routes> 和 <Route> 是 React Router 中主要的配置类型。可以把 <Route>看做是一个 if 判断:如果 path 匹配了当前 URL, 则会渲染他的 element!使用 <Route caseSensitive>可以启用路由大小写匹配。

当 location 变化时, <Routes> 会遍历下属 children 的所有 <Route> 以试图寻找最佳匹配的路由. <Route> 所在路径跟嵌套 UI 的路径位置是一致的,所见即所得的渲染方式。

例子:

<Routes>
  <Route path="/" element={<Dashboard />}>
    <Route
      path="messages"
      element={<DashboardMessages />}
    />
    <Route path="tasks" element={<DashboardTasks />} />
  </Route>
  <Route path="about" element={<AboutPage />} />
</Routes>

Note:

Routes 的 Javascript替代品见  useRoutes .

<Route element> 会默认往下渲染,并且拼接路径,所以不是每一个 Route 都需要配置一个 element 属性。

比如说下面的例子, 路径 /users/:id依然可以匹配到 UserProfile 元素,而不用担心路径被截断:

<Route path="users">
  <Route path=":id" element={<UserProfile />} />
</Route>

『完』

Was this page helpful?