React入門 02: ライブラリMarkedでMarkdownの機能を加える
Tags: javascript, webdev, tutorial, react
Published: 2019-12-09 12:52:39

---
title: React入門 02: ライブラリMarkedでMarkdownの機能を加える
published: true
description: Reactで差し込む要素のテキストを、Markdownで書いてみます。MarkdownのテキストをHTMLの記述に変えるために使うのは、JavaScriptライブラリMarkedです。また、Reactで生のHTMLコードを差し込む場合の特別な仕組みについてもご説明します。
tags: #javascript, #webdev, #tutorial, #react
---

「React入門 01: コンポーネントを組み立てる」(以下「React入門 01」)では、簡単なコンポーネントを組み合わせて、シンプルなSPAのページをつくりました。今回は、Reactで差し込む要素のテキストを、Markdownで書いてみます。MarkdownのテキストをHTMLの記述に変えるために使うのは、JavaScriptライブラリ[Marked](https://github.com/markedjs/marked#marked)です。また、Reactで生のHTMLコードを差し込む場合の特別な仕組みについてもご説明します。


# Markdown記法とMarkedのインストール
「[Markdown](https://www.markdown.jp/what-is-markdown/)」は、テキストの書式を定める簡易な記法です。HTMLコードよりも簡単な書き方で、文字や段落の表記が整えられます。JavaScriptライブラリMarkedは、npmでReactアプリケーションのディレクトリにつぎのようにインストールします。

```terminal
npm install marked --save
```

# `marked()`関数でMarkdownをHTMLの記述に変える

「React入門 01」で書いたJavaScriptコードに手を加えてゆきましょう。まず、`src/components/CommentList.js`のテキストにつぎのようにMarkdown記法を加えます。`*`(アスタリスク)と`_`(アンダースコア)は同じ働きです。それを確かめるために、あえて両方用いました。記号ひとつで文字を挟めば強調・斜体(`<em>`)、ふたつなら強い重要性・太字(`<strong>`)になります。

```react
const CommentList = () => {
  return (
    <div className="CommentList">
      <Comment author="ヘンリー・キッシンジャー">
      チャンスは__貯金__できない。
      </Comment>
      <Comment author="マーク・トウェイン">
      禁煙なんてたやすい。私は*何千回*もやった。
      </Comment>
    </div>
  );
};
```

テキストは子コンポーネント(`src/components/Comment.js`)が受け取りますので、MarkdownテキストをHTMLの記述に変換するコードは、項目のコンポーネント(`Comment`)に書き加えます。Markedライブラリから`import`するのは関数`marked()`です。引数に渡したMarkdownのテキストをHTMLの記述にして返します。なお、引数は文字列でなければならないので、`children`で得た子要素は`toString()`メソッドで文字列にしておきましょう。

ところが、JavaScriptコードをつぎのように書き替えて、marked()関数の戻り値をそのまま差し込むと、タグがテキストとして示されてしまいます(図001)。

```react
import marked from 'marked';

const Comment = (props) => {
  return (
    <div className="Comment">

      {marked(props.children.toString())}
    </div>
  );
};
```

#### 図001■タグがテキストとして表示される
![02_003.png](https://thepracticaldev.s3.amazonaws.com/i/r0eed2193iaoa61wanqh.png)


# `dangerouslySetInnerHTML`によりHTMLのコードを差し込む
生のHTMLコードが差し込めてしまうと、「[クロスサイトスクリプティング](http://e-words.jp/w/クロスサイトスクリプティング.html)」(XSS)による攻撃を受けるかもしれません(「[クロスサイトスクリプティング対策 ホンキのキホン](https://codezine.jp/article/detail/9149)」参照)。そのため、ReactはHTMLのタグは、そのままでは加えられないようにしたのです。テキストをHTMLとして差し込むためには、[`dangerouslySetInnerHTML`](https://ja.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml)プロパティを用いなければなりません(「[ReactのdangerouslySetInnerHTML使ってみた](https://qiita.com/hiromoon/items/f3ed77abd338139ba97b)」参照)。与えるのはオブジェクトで、プロパティ`__html`にHTMLコードを値として定めます。

もっとも、`dangerouslySetInnerHTML`プロパティにオブジェクトを直に与えるのは安全といえません。別に定めたメソッド(`rawMarkup()`)を呼び出し、その本体でHTMLコードをつくって返すのがよいでしょう。そこで、コンポーネント(`Comment`)はさらにつぎのように書き替えます。プロパティに与えるメソッドの呼び出しは、波かっこ`{}`でくくってください。こうすれば、コンポーネントの要素にはメソッドから返されたコードが、HTMLとして描かれるはずです(図002)。

```react
const Comment = (props) => {
  return (
    <div className="Comment">

      {/* {marked(props.children.toString())} */}
      <span dangerouslySetInnerHTML={rawMarkup(props.children.toString())} />
    </div>
  );
};

const rawMarkup = (markup) => {
  const rawMarkup = marked(markup);
  return { __html: rawMarkup };
};
```

#### 図002■MarkdownテキストがHTMLとして描かれる
![02_004.png](https://thepracticaldev.s3.amazonaws.com/i/955pp236dwlpbcp5xsom.png)

書き改めたふたつのコンポーネントのJavaScriptコードは、それぞれ以下のコード001と002にまとめました。

#### コード001■`src/components/CommentList.js`
```react
import React from 'react';
import Comment from './Comment';

const CommentList = () => {
  return (
    <div className="CommentList">
      <Comment author="ヘンリー・キッシンジャー">
      チャンスは__貯金__できない。
      </Comment>
      <Comment author="マーク・トウェイン">
      禁煙なんてたやすい。私は*何千回*もやった。
      </Comment>
    </div>
  );
};

export default CommentList;
```

#### コード002■`src/components/Comment.js`
```react
import React from 'react';
import marked from 'marked';

const Comment = (props) => {
  return (
    <div className="Comment">
      <h2 className="CommentAuthor">
      {props.author}
      </h2>
      <span dangerouslySetInnerHTML={rawMarkup(props.children.toString())} />
    </div>
  );
};

const rawMarkup = (markup) => {
  const rawMarkup = marked(markup);
  return { __html: rawMarkup };
};

export default Comment;
```