m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

Reactjsのチュートリアルを試す

Reactjsって?

javascriptの仮想DOMを使用しているフレームワーク
angularjsとかのディレクティブみたいなもん
jsxとかいう言語形態で使っているけどjsに変換は可能、またchromeで動かすこともできる

チュートリアル

日本語訳

React.jsチュートリアル【日本語翻訳】 | mae's blog
→わかりやすいと思います

英語読めないのはなんとかしなくてはと思いつつ日本語の方が助かっている…

チュートリアル

sample1
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample1</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * CommentBoxというコンポーネントを作成
     * React.createClassで作成する
     */
    var CommentBox = React.createClass({
        render: function() {
            return (
                    <div className="commentBox">
                        Hello, world! I am a CommentBox.
                    </div>
            );
        }
    });
    React.render(
            <CommentBox />,
            document.getElementById('content')
    );
</script>
</body>
</html>

仮想DOMの貼り方
とっても簡単です(^_^;)

sample2
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample2</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * CommentBoxというコンポーネントの中に
     * CommentList、CommentFormのコンポーネントを配置してコンポーネントを連鎖的に生成
     */
    var CommentBox = React.createClass({
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList />
                        <CommentForm />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {
            return (
                    <div className="commentList">
                        Hello, world! I am a CommentList.
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({
        render: function() {
            return (
                    <div className="commentForm">
                        Hello, world! I am a CommentForm.
                    </div>
            );
        }
    });

    React.render(
            <CommentBox />,
            document.getElementById('content')
    );
</script>
</body>
</html>

コンポーネントコンポーネントを貼れます
だんだん冗長しそうな感じ(´・ω・`)

sample3
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample3</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * CommentListというコンポーネントの中に
     * Commentコンポーネントを用意、 authorなどの属性を用意して
     * Commentコンポーネントの中で使用する
     */
    var CommentBox = React.createClass({
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList />
                        <CommentForm />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {
            return (
                    <div className="commentList">
                        <Comment author="Pete Hunt">This is one comment</Comment>
                        <Comment author="Jordan Walke">This is *another* comment</Comment>
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({
        render: function() {
            return (
                    <div className="commentForm">
                        Hello, world! I am a CommentForm.
                    </div>
            );
        }
    });

    var Comment = React.createClass({
        render: function() {
            return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}
                        </h2>
                        {this.props.children}
                    </div>
            );
        }
    });

    React.render(
            <CommentBox />,
            document.getElementById('content')
    );
</script>
</body>
</html>

属性や子要素の値を設定できます

sample4
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample4</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
    <script src="js/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * Commentコンポーネントの中で使用する文字列をマークダウン化する
     * しかし、通常の方法ではエスケープするのでhtmlに変換する
     * ※リスクが高いので気をつける必要があります
     */
    var CommentBox = React.createClass({
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList />
                        <CommentForm />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {
            return (
                    <div className="commentList">
                        <Comment author="Pete Hunt">This is one comment</Comment>
                        <Comment author="Jordan Walke">This is *another* comment</Comment>
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({
        render: function() {
            return (
                    <div className="commentForm">
                        Hello, world! I am a CommentForm.
                    </div>
            );
        }
    });

    var Comment = React.createClass({
        render: function() {
            // マークダウンしたhtmlを格納
            var rawMarkup = marked(this.props.children.toString(), {sanitize: true});

            return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}
                        </h2>
                        <!-- htmlの形式のまま表示 -->
                        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
                    </div>
            );
        }
    });

    React.render(
            <CommentBox />,
            document.getElementById('content')
    );
</script>
</body>
</html>

HTMLをそのまま表示したい場合はXSSに気をつける必要があるので気をつけてね

sample5
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample5</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
    <script src="js/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * 静的に定義されたデータをコンポーネントにバインドして表示
     */
    var CommentBox = React.createClass({
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList data={this.props.data} />
                        <CommentForm />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {

            // this.props.dataをmap処理で定義
            var commentNodes = this.props.data.map(function (comment) {
                return (
                        <Comment author={comment.author}>
                            {comment.text}
                        </Comment>
                );
            });

            return (
                    <div className="commentList">
                        {commentNodes}
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({
        render: function() {
            return (
                    <div className="commentForm">
                        Hello, world! I am a CommentForm.
                    </div>
            );
        }
    });

    var Comment = React.createClass({
        render: function() {
            // マークダウンしたhtmlを格納
            var rawMarkup = marked(this.props.children.toString(), {sanitize: true});

            return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}
                        </h2>
                        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
                    </div>
            );
        }
    });

    // バインドするデータを準備
    var data = [
        {author: "Pete Hunt", text: "This is one comment"},
        {author: "Jordan Walke", text: "This is *another* comment"}
    ];

    React.render(
            // データを割り当て
            <CommentBox data={data} />,
            document.getElementById('content')
    );
</script>
</body>
</html>

単方向バインディングができます。

sample6
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample6</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
    <script src="js/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * 静的に定義されたデータをコンポーネントにバインドして表示
     */
    var CommentBox = React.createClass({

        // データ読み込み処理
        loadCommentsFromServer: function() {
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                cache: false,
                success: function(data) {
                    this.setState({data: data});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });
        },

        // 初期
        getInitialState: function() {
            return {data: []};
        },

        // コンポーネントがレンダリングされた場合に呼ばれる
        componentDidMount: function() {
            this.loadCommentsFromServer();
            setInterval(this.loadCommentsFromServer, this.props.pollInterval);
        },

        // 描画
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList data={this.state.data} />
                        <CommentForm />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {

            // this.props.dataをmap処理で定義
            var commentNodes = this.props.data.map(function (comment) {
                return (
                        <Comment author={comment.author}>
                            {comment.text}
                        </Comment>
                );
            });

            return (
                    <div className="commentList">
                        {commentNodes}
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({
        render: function() {
            return (
                    <div className="commentForm">
                        Hello, world! I am a CommentForm.
                    </div>
            );
        }
    });

    var Comment = React.createClass({
        render: function() {
            // マークダウンしたhtmlを格納
            var rawMarkup = marked(this.props.children.toString(), {sanitize: true});

            return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}
                        </h2>
                        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
                    </div>
            );
        }
    });

    // バインドするデータを準備
    var data = [
        {author: "Pete Hunt", text: "This is one comment"},
        {author: "Jordan Walke", text: "This is *another* comment"}
    ];

    React.render(
            // URLを割り当て(ajaxするためのファイル名を設定)
            <CommentBox url="comments.json" pollInterval={2000} />,
            document.getElementById('content')
    );
</script>
</body>
</html>

ajaxの処理を使用したデータのバインディングも可能です。初期データが必要なので初期値を定義することが必要になります

sample7
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Sample7</title>
    <script src="js/react.js"></script>
    <script src="js/JSXTransformer.js"></script>
    <script src="js/jquery-2.1.4.min.js"></script>
    <script src="js/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
    /**
     * 静的に定義されたデータをコンポーネントにバインドして表示
     */
    var CommentBox = React.createClass({

        // データ読み込み処理
        loadCommentsFromServer: function() {
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                cache: false,
                success: function(data) {
                    this.setState({data: data});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });
        },

        // 初期
        getInitialState: function() {
            return {data: []};
        },

        // コンポーネントがレンダリングされた場合に呼ばれる
        componentDidMount: function() {
            this.loadCommentsFromServer();
            setInterval(this.loadCommentsFromServer, this.props.pollInterval);
        },

        // submit
        handleCommentSubmit: function(comment) {

            var comments = this.state.data;
            var newComments = comments.concat([comment]);
            this.setState({data: newComments});

            // post処理
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                type: 'POST',
                data: comment,
                success: function(data) {
                    this.setState({data: data});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });

        },

        // 描画
        render: function() {
            return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList data={this.state.data} />
                        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
                    </div>
            );
        }
    });

    var CommentList = React.createClass({
        render: function() {

            // this.props.dataをmap処理で定義
            var commentNodes = this.props.data.map(function (comment) {
                return (
                        <Comment author={comment.author}>
                            {comment.text}
                        </Comment>
                );
            });

            return (
                    <div className="commentList">
                        {commentNodes}
                    </div>
            );
        }
    });

    var CommentForm = React.createClass({

        handleSubmit: function(e) {
            //
            e.preventDefault();

            // 値を取得してtrim
            var author = React.findDOMNode(this.refs.author).value.trim();
            var text = React.findDOMNode(this.refs.text).value.trim();
            if (!text || !author) {
                return;
            }

            // post処理
            this.props.onCommentSubmit({author: author, text: text});

            // クリア
            React.findDOMNode(this.refs.author).value = '';
            React.findDOMNode(this.refs.text).value = '';
            return;
        },

        render: function() {
            return (
                    <form className="commentForm" onSubmit={this.handleSubmit}>
                        <input type="text" placeholder="Your name" ref="author" />
                        <input type="text" placeholder="Say something..." ref="text" />
                        <input type="submit" value="Post" />
                    </form>
            );
        }
    });

    var Comment = React.createClass({
        render: function() {
            // マークダウンしたhtmlを格納
            var rawMarkup = marked(this.props.children.toString(), {sanitize: true});

            return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}
                        </h2>
                        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
                    </div>
            );
        }
    });

    // バインドするデータを準備
    var data = [
        {author: "Pete Hunt", text: "This is one comment"},
        {author: "Jordan Walke", text: "This is *another* comment"}
    ];

    React.render(
            // URLを割り当て(ajaxするためのファイル名を設定)
            <CommentBox url="comments.json" pollInterval={2000} />,
            document.getElementById('content')
    );
</script>
</body>
</html>

サーバにpostする場合ことも可能、基本的にはReactJSの機能ではありません
イベントに応じて機能を使用できる

所感

仮想DOMを使用する部分だけに制限するならあんまり学習コストはかからないイメージ。
チュートリアルしかやっていないのでまだいろいろと調べる必要があるかもしれないけど
jqueryなどと切り離されているので一部のみを使用するというかんがえならありかもしれないと思う