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
にまとめます。
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
を使って ChakraLink
と RouterLink
としてインポートし、統合します。
この時点では、当然props
の型を指定していないため型エラーとなります。
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が生成されます。
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
を引数に指定するのが正解なんだろう」
と直感的に思ってしまいました。
しかし、ここに落とし穴があります。
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
)を指定して呼び出すと型エラーになります。
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型を作る必要があります。
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
の機能を統合したコンポーネントを使えるようになりました。