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

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark