React + TypeScriptでMarkdownをMathjax + Highlight付きで表示する（メモ）

# こういう時、あるよね？（たぶん）

React + TypeScriptで、サーバー上に置いたMarkdownを綺麗に表示させたい！（数式、コードも）

で、結構詰まったのでメモ。

# ファイルの読み込み

XMLHttpRequestを使って、"source"にMarkdownを読み込みます。

```tsx:src/Md.tsx
import React, { useEffect } from 'react';
import './Md.css'

export default function Md() {
    const [source, setSource] = React.useState<string>("");
    useEffect(() => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "./Problems/p1.md");
        xhr.send();
        xhr.onload = function () {
            if (xhr.status !== 200) {
                alert("Error");
            } else {
                setSource(xhr.responseText);
            }
        }
    }, [])
    return (
        <div className="main">
            \(ax^2+bx+c=0\)
        </div>
    );
}
```

# Mathjaxを入れる

index.htmlに、これを入れるだけです。簡単！

```html
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax
/2.7.7/MathJax.js?config=TeX-AMS_CHTML"></script>
```

さっきの

```
\(ax^2+bx+c=0\)
```

が数式っぽく表示されるようになりました。

# MarkdownをHTMLに変換して表示する

これはなんでかわからないんですが、

```
npm i --save @types/marked marked
```
でJSとTS両方入れておきます。どっちか欠けてるとできませんでした。なんでだろ

tsxファイルは以下のように修正します。

```tsx:src/Md.tsx
import React, { useEffect } from 'react';
import marked from 'marked';
import './Md.css'

export default function Md() {
    const [source, setSource] = React.useState<string>("");
    useEffect(() => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "./Problems/p1.md");
        xhr.send();
        xhr.onload = function () {
            if (xhr.status !== 200) {
                alert("Error");
            } else {
                setSource(marked(xhr.responseText));
            }
        }
    }, [])
    return (
        <div className="main">
            <div dangerouslySetInnerHTML={{ __html: source }} />
        </div>
    );
}
```

これで数式が表示されるはず...と思ったらされなくて、数式のバックスラッシュがmarkedで消えてしまうっぽいのでバックスラッシュを2本書くようにします。

# Syntax Highlightを付ける

コードを表示する時、Syntax Highlight欲しいですよね？私は欲しいです。

そこで、highlight.jsを使います。

```
npm i --save hightlight.js @types/hightlight.js
```

後は、これでOKです。

ハイライトのスタイルはいっぱいあるので、色々試してみて下さい。

```tsx:src/Md.tsx
import React, { useEffect } from 'react';
import marked from 'marked';
import highlightjs from 'highlight.js';
import 'highlight.js/styles/atelier-lakeside-dark.css'
import './Md.css'

marked.setOptions({
    highlight: function (code, lang) {
        return highlightjs.highlightAuto(code, [lang]).value;
    },
});

export default function Md() {
    const [source, setSource] = React.useState<string>("");
    useEffect(() => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "./Problems/p1.md");
        xhr.send();
        xhr.onload = function () {
            if (xhr.status !== 200) {
                alert("Error");
            } else {
                setSource(marked(xhr.responseText));
            }
        }
    }, [])
    return (
        <div className="main">
            <div dangerouslySetInnerHTML={{ __html: source }} />
            \(ax^2+bc+c=0\)
        </div>
    );
}
```

これで、無事にMarkdownを表示できました。やったー！

コードのフォントはUbuntu Monoを使いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528439/a2a0e608-5b4b-d5a3-24e8-22d5830e47ab.png)


(2021/2/28 追記)
記事中の```dangerouslySetInnerHTML```を使うとXSSを引き起こす可能性があります。ユーザ入力を扱う際は気をつけて下さい。