LoginSignup
3
1

More than 1 year has passed since last update.

【React + TypeScript】 Chakra UI の Link と React Router v6 の Link を統合する

Last updated at Posted at 2022-03-30

ReactでUIライブラリ「Chakra UI 」の Link コンポーネントと ルーティングライブラリ「React Router v6」 の Link コンポーネントを統合したものを、TypeScriptのTSXとして記述する方法をご紹介します。

ライブラリのバージョン

"react": "^17.0.2",
"@chakra-ui/react": "^1.8.7,
"react-router-dom": "6",

結論

React.ComponentProps<typeof コンポーネント名> を使って Chakra UI のLinkの型とReact RouterのLinkの型を割り出して、LinkPropsにまとめます。

Link.tsx (最終結果)
import React from "react"
import { Link as ChakraLink } from "@chakra-ui/react"
import { Link as RouterLink } from "react-router-dom"

type LinkProps = React.ComponentProps<typeof ChakraLink> & React.ComponentProps<typeof RouterLink>

export const Link = ({ children, ...props }: LinkProps) => {
  return (
    <ChakraLink as={RouterLink} {...props}>
      {children}
    </ChakraLink>
  )
}

考え方

公式によると……

公式ドキュメントより転載
// 1. import { Link as ReachLink } from "@reach/router"

// 2. Then use it like this
<Link as={ReachLink} to='/home'>
  Home
</Link>

こちらの公式ドキュメントに載っているものは Reach Router ライブラリのLinkコンポーネントと統合する方法であることと、JavaScriptのJSXでの記述法になっています。

途中経過

この方法にならって、両者の Linkコンポーネント を as を使って ChakraLinkRouterLink としてインポートし、統合します。
この時点では、当然propsの型を指定していないため型エラーとなります。

Link.tsx (途中経過)
import { Link as ChakraLink } from "@chakra-ui/react"
import { Link as RouterLink } from "react-router-dom"

export const Link = ({ children, ...props }) => { // 'children' is missing in props validation
  return (
    <ChakraLink as={RouterLink} {...props}> 
      {children}
    </ChakraLink>
    //プロパティ 'to' は型 '{ children: any; as: ForwardRefExoticComponent<LinkProps & RefAttributes<HTMLAnchorElement>>; }'にありませんが、
    // 型 'OmitCommonProps<LinkProps & RefAttributes<HTMLAnchorElement>, keyof LinkProps>' では必須です。ts(2741)
  )
}

勘違いでPropsの型にハマる

Chakra UI では、コンポーネントのas引数にHTMLタグ名(a,divなど)を指定すると、そのタグ扱いでDOMが生成されます。

Div.tsx
import { Box } from "@chakra-ui/react"

export const Div = () => {
  return (
    <Box as="div">
      あいうえお
    </Box>
  )
}
表示結果
<div class="css-0">あいうえお</div>

そこで、今回のケースを考えたとき、

ChakraLink as={RouterLink}と書いているので、Chakra UIのLinkがReact RouterのLinkが扱いになる。
だから、Propsの型にはReact RouterのLink用のProps型 LinkProps を引数に指定するのが正解なんだろう」

と直感的に思ってしまいました。
しかし、ここに落とし穴があります。

Link.tsx (失敗例)
import { Link as ChakraLink } from "@chakra-ui/react"
import { Link as RouterLink, LinkProps } from "react-router-dom"

export const Link = ({ children, ...props }: LinkProps) => {
  return (
    <ChakraLink as={RouterLink} {...props}>
      {children}
    </ChakraLink>
  )
}

この統合されたLinkコンポーネントに Chakra UI コンポーネント用のCSSを設定する引数(以下の場合はfontSize)を指定して呼び出すと型エラーになります。

Parent.tsx (親コンポーネント)
import { Link } from "components/link"
export const LogInLink: React.FC = () => {
  return (
    <Link mx="2" fontSize="2rem" to="/auth/login">
      ログイン
    </Link>
    // 型 '{ children: string; mx: string; fontSize: string; to: string; }' を
    // 型 'IntrinsicAttributes & LinkProps' に割り当てることはできません。
    // プロパティ 'mx' は型 'IntrinsicAttributes & LinkProps' に存在しません。
  )
}

これは、統合されたLinkコンポーネントの引数型には、React RouterのLink用のProps型・LinkPropsは指定されているが、Chakra UIのLink用のProps型が含まれていないことが原因です。
よって、両者の引数を受け取れるように、ふたつのLinkコンポーネントのPropsを統合したProps型を作る必要があります。

Link.tsx (最終結果)
import React from "react"
import { Link as ChakraLink } from "@chakra-ui/react"
import { Link as RouterLink } from "react-router-dom"

type LinkProps = React.ComponentProps<typeof ChakraLink> & React.ComponentProps<typeof RouterLink>

export const Link = ({ children, ...props }: LinkProps) => {
  return (
    <ChakraLink as={RouterLink} {...props}>
      {children}
    </ChakraLink>
  )
}

以上で、問題なく外部で両者のLinkの機能を統合したコンポーネントを使えるようになりました。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1