homehome

react-markdown v6の変更点

Published

TocMenu目次

react-markdownのメジャーアップデートv6がきており、v5からいくつか仕様が変更されていた。既存コードを書き直す必要があったので、その際の変更点を残しておく。環境は次のとおりです。

  • react-markdown(6.0.0)
  • react-styntax-highlight(15.4.3)
  • Next.js(10.1.3)
  • theme-ui(0.7.0)

主要な変更点

sourceが廃止され、children propsになった。

<Markdown>{`source`}</Markdown>

rendererscomponents propsに変更された。これに伴いコンバートさせるコンポーネント名もいくつか変更された。例えばimageimageReferenceは両者ともimgになった。

<Markdown
  components={{
    code: CodeBlock,
    img: BlockImage,
  }}>
  {...}
</Markdown>

allowDangerousHtmlが削除された。同様のことを行うにはrephy-rawを使う必要があるらしい。

コードブロック

react-syntax-highlighterを併用すると、v5のままでは動作しなくなった。

ちなみにv5ではコードブロックは以下のようにしていた。

import { PrismAsyncLight as SyntaxHighlighter } from "react-syntax-highlighter";
import nord from "react-syntax-highlighter/dist/esm/styles/prism/nord";

const CodeBlock = ({ language, value }) => {
  const styles = { margin: "16px 0" };
  return (
    <SyntaxHighlighter language={language} style={nord} customStyle={styles}>
      {value}
    </SyntaxHighlighter>
  );
};

v5ではCodeBlock関数で受け取るpropsは次のようなものだった。コードブロック(複数行)のみ捜査され、インラインコードは対象外。

{
  children: "",
  language: "language-bash",
  node: object,
  value: "ls -la",
}

一方、v6ではコードブロックは次のようなpropsを受け取る。

{
  children: ["..."],
  className: "language-bash",
  node: object,
}

そしてv6はインラインコードも捜査され、以下のようなpropsを受け取る。

{
	children: ["..."],
	inline: true,
	node: object,
}

まとめると

  • v5のclassNameがv6のlanguageに対応
  • v5のvalueがv6のchildren[0]に対応
  • classNameはコードブロックのみに存在する
  • inlineはインラインコードにのみ存在する

公式のサンプル

公式にコードハイライトの例が以下のように記載されている。前述のようにコードブロックではclassName propsが存在するため、これを使い出力を分岐させている。

// 抜粋
const components = {
  code({node, className, ...props}) {
    const match = /language-(\w+)/.exec(className || '')
    return match
      ? <SyntaxHighlighter language={match[1]} PreTag="div" style={dark} {...props} />
      : <code className={className} {...props} />
  }
}

render(<ReactMarkdown components={components} children={markdown} />, document.body)

自分のプロジェクトに組み込んでみると、動作はするが2点問題が起こった。

  • コンソールにインラインエラーが出る
  • コードブロック下部に空白ができる

コンソールのインラインエラー

開発ツールで以下のようなエラーが表示される。

Warning: Received true for a non-boolean attribute inline. If you want to write it to the DOM, pass a string instead: inline="true" or inline={value.toString()}.

これはinlineおよび{...props}が原因。手っ取り早く<code className={className} />にするか、次のようにinlineを切り分けるといい。

const components = {
  code({node, className, inline, ...props}) {
    const match = /language-(\w+)/.exec(className || '')
    return match
      ? <SyntaxHighlighter language={match[1]} PreTag="div" style={dark} {...props} />
      : <code className={className} {inline.toString()} {...props} />
  }
}

コードブロック下の空白

スタイルをリセットしても生じるため、ややハマった問題。

codeblock-space

これはコードブロックのchildren propsの末尾に"\n"が追加されているのが原因。つまり余分な1行が生成されている。解決にはこの改行コードを削除する。

<SyntaxHighlighter language={match[1]} style={dark} PreTag="div">
	{children[0].replace(/\n$/i, "")}
</SyntaxHighlighter>

preの増殖

v5までは生成されるコードがpre > codeだったが、v6ではpre > pre > codeになっている。1番上のpreはreact-markdownが生成しているもので、その下のpreはsyntex-hilighterが生成している。react-syntax-highlighter側のpreTagでタグを変更しないと、二重preが出来上がってしまう。

<SyntaxHighlighter PreTag="div" ... />

また、react-syntax-highlighterにはcustomstyleという最上位のpreのスタイルを上書きするpropsがある。v5まではこれ用いてコードブロック全体をラップするスタイルを設定できていたが、v6では前述のようにreact-markdownが生成したpreが最上位にあるため、親preにスタイルを適用させる方法がなくなっている。

このようにv6は、使い勝手が幾分悪くなっている印象を受ける。