Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

自定义链接 (Custom Link)

虽然在很多情况下重复代码是可以接受的,但你可能会发现自己重复得太频繁了。有时,你希望创建具有额外行为或样式的跨切面(cross-cutting)组件,或者希望将第三方 UI 库与 TanStack Router 的类型安全特性结合使用。

createLink 可以创建一个与原生 Link 具有相同类型参数的自定义链接组件。这意味着你可以创建自己的组件,同时获得与官方 Link 相同的类型安全保障和 TypeScript 性能。

基础示例

如果你想创建一个基础的自定义链接组件,可以参考以下代码:

import * as React from "react";
import { createLink, LinkComponent } from "@tanstack/react-router";

interface BasicLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
  // 在此处添加任何你想传递给 anchor 元素的额外 Props
}

const BasicLinkComponent = React.forwardRef<HTMLAnchorElement, BasicLinkProps>(
  (props, ref) => {
    return (
      <a ref={ref} {...props} className={"block px-3 py-2 text-blue-700"} />
    );
  },
);

// 使用 createLink 包装你的基础组件
const CreatedLinkComponent = createLink(BasicLinkComponent);

// 导出具备类型安全的自定义 Link
export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
  return <CreatedLinkComponent preload={"intent"} {...props} />;
};

现在你可以像使用普通 Link 一样使用你的 CustomLink,它会自动提示路径和参数:

<CustomLink to={"/dashboard/invoices/$invoiceId"} params={{ invoiceId: 0 }} />

以下是将 createLink 与流行第三方 UI 库结合使用的示例。

React Aria Components 示例

React Aria Components (v1.11.0+) 支持 TanStack Router 的 preload (intent) 属性。你可以使用 createLink 包装任何作为链接使用的 RAC 组件。

import { createLink } from "@tanstack/react-router";
import { Link as RACLink, MenuItem } from "react-aria-components";

export const Link = createLink(RACLink);
export const MenuItemLink = createLink(MenuItem);
import { createLink } from "@tanstack/react-router";
import { Link as RACLink, type LinkProps } from "react-aria-components";

interface MyLinkProps extends LinkProps {
  // 你的自定义属性
}

function MyLink(props: MyLinkProps) {
  return (
    <RACLink
      {...props}
      style={({ isHovered }) => ({
        color: isHovered ? "red" : "blue",
      })}
    />
  );
}

export const Link = createLink(MyLink);

Chakra UI 示例

import * as React from "react";
import { createLink, LinkComponent } from "@tanstack/react-router";
import { Link } from "@chakra-ui/react";

interface ChakraLinkProps extends Omit<
  React.ComponentPropsWithoutRef<typeof Link>,
  "href"
> {}

const ChakraLinkComponent = React.forwardRef<
  HTMLAnchorElement,
  ChakraLinkProps
>((props, ref) => <Link ref={ref} {...props} />);

const CreatedLinkComponent = createLink(ChakraLinkComponent);

export const CustomLink: LinkComponent<typeof ChakraLinkComponent> = (
  props,
) => {
  return (
    <CreatedLinkComponent
      textDecoration={"underline"}
      _hover={{ textDecoration: "none" }}
      _focus={{ textDecoration: "none" }}
      preload={"intent"}
      {...props}
    />
  );
};

MUI (Material UI) 示例

MUI 的集成非常灵活。如果只是简单的包装:

import { createLink } from "@tanstack/react-router";
import { Link } from "@mui/material";

export const CustomLink = createLink(Link);

如果需要使用 MUI 的 Button 作为链接(注意设置 component="a"):

import React from "react";
import { createLink, LinkComponent } from "@tanstack/react-router";
import { Button, type ButtonProps } from "@mui/material";

interface MUIButtonLinkProps extends ButtonProps<"a"> {}

const MUIButtonLinkComponent = React.forwardRef<
  HTMLAnchorElement,
  MUIButtonLinkProps
>((props, ref) => <Button ref={ref} component="a" {...props} />);

const CreatedButtonLinkComponent = createLink(MUIButtonLinkComponent);

export const CustomButtonLink: LinkComponent<typeof MUIButtonLinkComponent> = (
  props,
) => {
  return <CreatedButtonLinkComponent preload={"intent"} {...props} />;
};

Mantine 示例

import * as React from "react";
import { createLink, LinkComponent } from "@tanstack/react-router";
import { Anchor, AnchorProps } from "@mantine/core";

interface MantineAnchorProps extends Omit<AnchorProps, "href"> {}

const MantineLinkComponent = React.forwardRef<
  HTMLAnchorElement,
  MantineAnchorProps
>((props, ref) => <Anchor ref={ref} {...props} />);

const CreatedLinkComponent = createLink(MantineLinkComponent);

export const CustomLink: LinkComponent<typeof MantineLinkComponent> = (
  props,
) => {
  return <CreatedLinkComponent preload="intent" {...props} />;
};