こういう時、あるよね?(たぶん)
React + TypeScriptで、サーバー上に置いたMarkdownを綺麗に表示させたい!(数式、コードも)
で、結構詰まったのでメモ。
ファイルの読み込み
XMLHttpRequestを使って、"source"にMarkdownを読み込みます。
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に、これを入れるだけです。簡単!
<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ファイルは以下のように修正します。
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です。
ハイライトのスタイルはいっぱいあるので、色々試してみて下さい。
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を使いました。
(2021/2/28 追記)
記事中のdangerouslySetInnerHTML
を使うとXSSを引き起こす可能性があります。ユーザ入力を扱う際は気をつけて下さい。