LoginSignup
4
5

More than 5 years have passed since last update.

React: マークダウンの複数コメントをHTMLに変換してリストにする例

Last updated at Posted at 2018-12-05

本稿では、Reactで簡単なリストをつくってみます。コンポーネントはクラスで定め、複数データをリストにするというサンプルです。また、gumi Inc. Advent Calendar 2018の12月2日「ReactのdangerouslySetInnerHTML使ってみた」を受けて、マークダウンのテキストをライブラリで変換してページに差し込んでみます。

See the Pen React: Formatting text with markdown by Fumio Nonaka (@FumioNonaka) on CodePen.

create-react-appでReactのひな形アプリケーションをつくる

まず、Reactアプリケーションのジェネレータcreate-react-appで、my-appという名前のひな形アプリケーションをつくります。create-react-appのインストールやひな形のつくりかたについては、「React: Visual Studio Codeで開発環境を整える」をお読みください。アプリケーション名のフォルダがつくられ、依存関係を含めた必要なファイルがつぎのように納められます。

qiita_12_002_003.png

コメントリストをつくる

ひな形のsrc/App.jsをつぎのように書き替えましょう。3つのコンポーネントをクラスで定めています。いずれのクラスも、備えているのはrender()メソッドだけです。メソッドの戻り値が、HTMLページに差し込まれるテンプレートになります。テンプレートの要素に属性のかたちで与えた値を取り出すのがプロパティpropsです。各コンポーネントのコードは少ないので、モジュール分けせずひとつのJavaScript(JS)ファイルとします。

import React, { Component } from 'react';
import './App.css';

class App extends Component {
    render() {
        return (
            <div className="comment-box">
                <h1>世界の金言</h1>
                <CommentList />
            </div>
        );
    }
}
class CommentList extends Component {
    render() {
        return (
            <div>
                <Comment author="ヘンリー・キッシンジャー">チャンスは__貯金__できない。</Comment>
                <Comment author="マーク・トウェイン">禁煙なんてたやすい。私は*何千回*もやった。</Comment>
            </div>
        );
    }
}
class Comment extends Component {
    render() {
        return (
            <div>
                <h2>
                {this.props.author}
                </h2>
                {this.props.children}
            </div>
        );
    }
}
export default App;

ブラウザでアプリケーションのページを開くと、テンプレートにしたがって要素の構造がつくられ、コメントのリストとして表示されます。

qiita_12_003_001.png

マークダウンのライブラリを使う

コメントのテキストにはアスタリスク(*)やアンダースコア(_)が入っています。これらをマークダウンで表示しようということです。ライブラリとしてはremarkableを使うことにします。README.mdにしたがって、npmでインストールしてください。

$ npm install remarkable --save

Remarkable()コンストラクタでつくったインスタンスにrender()メソッドでマークダウンテキストを渡せば、HTMLのフォーマットに変換されたテキストが返されます。コンポーネント(Comment)をつぎのように書き替えましょう。

import Remarkable from 'remarkable';

class Comment extends Component {
    markDown = new Remarkable();
    render() {
        return (
            <div>

                <span>
                {this.markDown.render(this.props.children.toString())}
                </span>
            </div>
        );
    }
}

ただし、このままではタグがテキストとして示されてしまいます。

qiita_12_003_002.png

dangerouslySetInnerHTMLプロパティによりHTMLのコードを差し込む

生のHTMLコードが差し込めてしまうと、「クロスサイトスクリプティング」(XSS)による攻撃を受けるかもしれません(「クロスサイトスクリプティング対策 ホンキのキホン」参照)。そのため、ReactはHTMLのタグは、そのままでは加えられないようにしたのです。

テキストをHTMLとして差し込むためには、dangerouslySetInnerHTMLプロパティを用いなければなりません。与えるのはオブジェクトで、プロパティ__htmlにHTMLコードを値として定めます(「ReactのdangerouslySetInnerHTML使ってみた」参照)。コンポーネント(Comment)はさらにつぎのように書き替えましょう。こうすれば、要素にはRemarkableのrender()メソッドから返されたマークダウンテキストが、HTMLとして描かれます。

class Comment extends Component {
    rawMarkup() {
        const markDown = new Remarkable();
        const rawMarkup = markDown.render(this.props.children.toString());
        return { __html: rawMarkup };
    }
    render() {
        return (
            <div>

                <span dangerouslySetInnerHTML={this.rawMarkup()} />
            </div>
        );
    }
}

qiita_12_003_003.png

配列データからテキストを取り出して差し込む

コメントとして表示するデータは、つぎのように配列にしてアプリケーション(App)のプロパティ(data)にもたせましょう。それを子コンポーネント(CommentList)の属性に与えます。そのデータは、Array.map()メソッドで取り出され、さらにその子のコンポーネント(Comment)のテンプレートがつくられるという流れです。アプリケーションのページの見た目は変わりません。

class App extends Component {
    data = [
        {id: 1, author: "ヘンリー・キッシンジャー", text: "チャンスは__貯金__できない。"},
        {id: 2, author: "マーク・トウェイン", text: "禁煙なんてたやすい。私は*何千回*もやった。"}
    ];
    render() {
        return (
            <div className="comment-box">
                <h1>世界の金言</h1>
                <CommentList data={this.data} />
            </div>
        );
    }
}
class CommentList extends Component {
    render() {
        const commentNodes = this.props.data.map((comment) => {
            return (
                <Comment author={comment.author} key={comment.id}>
                {comment.text}
                </Comment>
            );
        });
        return (
            <div>
            {commentNodes}
            </div>
        );
    }
}

ここまで書き終えたsrc/App.jsのコードは、以下にまとめたとおりです。冒頭のCodePenのサンプルでも中身はお確かめいただけます。ただし、ライブラリの読み込み方とその参照の仕方が、create-react-appでつくったアプリケーションと少し違いますので、その点だけお気をつけください。

import React, { Component } from 'react';
import Remarkable from 'remarkable';
import './App.css';

class App extends Component {
    data = [
        {id: 1, author: "ヘンリー・キッシンジャー", text: "チャンスは__貯金__できない。"},
        {id: 2, author: "マーク・トウェイン", text: "禁煙なんてたやすい。私は*何千回*もやった。"}
    ];
    render() {
        return (
            <div className="comment-box">
                <h1>世界の金言</h1>
                <CommentList data={this.data} />
            </div>
        );
    }
}
class CommentList extends Component {
    render() {
        const commentNodes = this.props.data.map((comment) => {
            return (
                <Comment author={comment.author} key={comment.id}>
                {comment.text}
                </Comment>
            );
        });
        return (
            <div>
            {commentNodes}
            </div>
        );
    }
}
class Comment extends Component {
    rawMarkup() {
        const markDown = new Remarkable();
        const rawMarkup = markDown.render(this.props.children.toString());
        return { __html: rawMarkup };
    }
    render() {
        return (
            <div>
                <h2>
                {this.props.author}
                </h2>
                <span dangerouslySetInnerHTML={this.rawMarkup()} />
            </div>
        );
    }
}
export default App;
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5