theme-ui / emotionでGatsby / React RouterなどのLinkタグを使う時の付け焼き刃
厳密には、「theme-ui / emotionなどを使ったライブラリで、Linkタグを任意のライブラリで動作させる」かもしれません。 あと、普通にany出てきます。よしなに解決する方法があればDMなりブコメで教えてもら […]
広告ここから
広告ここまで
目次
厳密には、「theme-ui / emotionなどを使ったライブラリで、Linkタグを任意のライブラリで動作させる」かもしれません。
あと、普通にany出てきます。よしなに解決する方法があればDMなりブコメで教えてもらえると嬉しいです。
やりたいこと
- UI FrameworkをReactで作る
- Gatsby / Next.js / React Routerなど様々なFWで使う想定
- NavigationなどでLink要素を含むComponentをバンドルする
問題となる点
sx
プロパティをtheme-ui
/emotion
側に渡す必要がある- 「使わないFWのLink Component」を読み込むとエラーになる可能性がある
- とはいえtheme-uiデフォルトも使いたい場合もある
やったこと
Context APIを使ってタグを外部から指定可能にする
今回のケースでは、Gatsby / theme-ui / React Routerどれかを使う可能性があったので、Union型で指定するようにしました。Consumerを使ってもいいのですが、useContext
派なので省いています。
import React, { createContext, FC, PropsWithChildren, useContext } from 'react';
import {NavLink as ThemeUINavLink, Link as ThemeUILink } from 'theme-ui'
import { Link as RRLink} from 'react-router-dom'
import { Link as GatsbyLink } from 'gatsby'
export type LinkTagType = 'a' | typeof GatsbyLink | typeof RRLink | typeof ThemeUINavLink | typeof ThemeUILink
const LinkTagContext = createContext<{
tag: LinkTagType
}>({
tag: 'a'
})
export const useLinkTag = () => useContext(LinkTagContext)
export const LinkTagProvider: FC<PropsWithChildren<{
linkType: LinkTagType
}>> = (props) => (
<LinkTagContext.Provider value={{tag: props.linkType}}>
{props.children}
</LinkTagContext.Provider>
)
createElementではなくjsx関数を利用する
動的に要素を作る場合はcreateElement
を使いますが、sx
プロパティを処理することができません。emotionやtheme-uiは独自にjsx
関数を用意していますので、そちらを使います。
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from 'theme-ui';
import { FC, ReactNode } from 'react';
import { Link as ThemeUILink } from 'theme-ui';
import { useLinkTag } from '../../lib/link-tag-provider/link-tag-provider';
type Link = {
href: string;
children: ReactNode;
kind?: string;
};
export const Link: FC<Link> = ({ href, children, kind }) => {
const { tag } = useLinkTag()
if (typeof tag !== 'string') {
return jsx(tag as any, {
to: href,
href,
children,
sx:{
color: 'inherit',
textDecoration: 'none',
}
})
}
return (
<ThemeUILink variant={kind} href={href}>
{children}
</ThemeUILink>
);
};
Providerを使ってLinkを切り替える
あとはProviderを使ってどのタグを利用するか指定しましょう。
export default (props) => (
<LinkTagProvider
linkType={Link}>
<pre><code>{JSON.stringify(props, null, 2)}</code></pre>
</LinkTagProvider>
)
あとはライブラリ内部で使っているLinkがちゃんと切り替わっているか確認しておきます。