gatsbyJavaScriptReact

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がちゃんと切り替わっているか確認しておきます。

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts