React使ってSPAを作るよ(3)

React使ってSPAを作るよ(3)

React使ってSPAを作るよ目次

React使ってSPAを作るよ(2)の続きです。

まずは残りのコンポーネントを書いてしまいます!

ド━(゚Д゚)━ン!!

var React = require('react');

//記事リンク
var ArticleLink = React.createClass({
  render: function() {
    var articleImage = {
      backgroundImage : "url(" + this.props.image + ")" 
    };
    return (
      <a href={this.props.articleUrl} target="_blank"  style={articleImage}>
        <h2>{this.props.articleTitle}</h2>
        <p>{this.props.articleDescription}</p>
      </a>
    );
  }
});

//記事リンクエリア
var ArticleArea = React.createClass({
  render: function() {
    return (
        <ArticleLink articleUrl="/" image="https://source.unsplash.com/random" articleTitle="記事タイトル" articleDescription="discription"></ArticleLink>
    );
  }
});

//タグ
var Tag = React.createClass({
  render: function() {
    return (
      <li data-tag={this.props.tag}><a href={this.props.tagUrl}>{this.props.tag}</a></li>
    );
  }
});

//タグ取得
var TagList = React.createClass({
  render: function() {
    return (
      <Tag tag="タグ1" tagUrl="/">タグ1</Tag>
    );
  }
});

//タグリスト
var TagArea = React.createClass({
  render: function() {
    return (
      <ul className="tag-list">
        <TagList />
      </ul>
    );
  }
});

//読んだ人
var Mark = React.createClass({
  render: function() {
    var accountImage = {
      backgroundImage : "url(" + this.props.image + ")" 
    };
    return (
      <li data-account={this.props.author} style={accountImage}></li>
    );
  }
});

//読んだ人取得
var MarkList = React.createClass({
  render: function() {
    return (
      <Mark author="Pete Hunt" image="https://source.unsplash.com/random"></Mark>
    );
  }
});

//読んだ人リスト
var MarkArea = React.createClass({
  render: function() {
    return (
      <ul className="account-list">
        <MarkList />
      </ul>
    );
  }
});

//コメント
var Comment = React.createClass({
  render: function() {
    var accountImage = {
      backgroundImage : "url(" + this.props.image + ")" 
    };
    return (
      <li><span data-account={this.props.author} style={accountImage}></span><span>{this.props.children}</span></li>
    );
  }
});

//コメント取得
var CommentList = React.createClass({
  render: function() {
    return (
      <Comment author="Pete Hunt" image="https://source.unsplash.com/random">コメント1</Comment>
    );
  }
});

//コメントリスト
var CommentArea = React.createClass({
  render: function() {
    return (
      <ul className="comment-list">
        <CommentList />
      </ul>
    );
  }
});

//記事リストコンポーネント
var ArticleList = React.createClass({
  render: function() {
    return (
    <li>
      <article>
        <ArticleArea />
        <TagArea />
        <h3>読んだ</h3>
        <MarkArea />
        <h3>コメント</h3>
        <CommentArea />
      </article>
    </li>
    );
  }
});

//レンダリング
React.render(
  <ArticleList />,
  document.getElementById('article-list')
);

って具合で、これをブラウザで確認すると、

react

それぞれのパーツが1個ずつ描画できました。

Reactで繰り返しを記述するには?

さてこのパーツ。
大枠の記事もリストにしたいし、タグやコメントも、データを取得して連続で描画したいですね。
繰り返しはfor文とか使うのかな?と思いきや、map関数を使うらしいです。
参考にさせていただいたのがこの記事。

JSONで外に出したデータを読み込んで、データの数だけ要素を生成します。

JSXのソースを見慣れていないのと、dataやcommentというのがいっぱいあって何がなんやらなので、ひとまずこっちのモックでもコメントリストの部分だけ使って実現してみたいと思います。

まずはJSONデータを用意します。
JSXの最初の方に置いておきましょう。

var commentData = [
      {
       author: "ユーザー1",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント1"
      },
      {
       author: "ユーザー2",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント2"
      },
      {
       author: "ユーザー3",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント3"
      }
];

コメントのコンポーネントを記述します。

    var Comment = React.createClass({
      render: function() {
      var authorImage = {
        backgroundImage : "url(" + this.props.authorImage + ")" 
      };
        return (
          <li>
            <span data-account={this.props.author} style={authorImage}></span><span>{this.props.comment}</span>
          </li>
        )
      }
    });

ここまでは前回やったのと同じ感じですね。
ここからが今回のポイントになる繰り返し用の記述です。

    var CommentList = React.createClass({
      render: function() {
        var commentNodes = this.props.data.map(function(comment) {
          return (
            <Comment author={comment.author} authorImage={comment.authorImage} comment={comment.comment}></Comment>
          )
        });
        return (
          <ul className="comment-list">
            {commentNodes}
          </ul>
        )
      }
    });

かんたんに言うと、commentNodeにmapでぐるぐるして生成したli要素たちをまとめてドカッと入れて、それをulで挟んで返しているわけですね!説明が適当すぎますね!私もいまだによく理解していません!

さらに、一番下のレンダリング部分。
ここで、定義されていないtagListなどを書くとエラーになっちゃうので、コメントリスト用の記述のみ残すことにします。
このとき、CommentListにdataを渡してあげます。

//記事リストコンポーネント
var ArticleList = React.createClass({
  render: function() {
    return (
    <li>
      <article>
        <CommentList data={commentData} />
      </article>
    </li>
    );
  }
});

/* レンダリング */
React.render(
  <ArticleList />,
  document.getElementById('article-list')
);

これでブラウザを見てみると、こんな感じになりました。

やったね!(∩´∀`)∩ワーイ

Chromeの開発ツールで見てみると、HTML構造もちゃんとモックのとおりになっています。

JSONを入れ子にする場合は…?

というわけでJSONのデータを読み込んでリストを繰り返し描画することはできたのですが、実際にはこのコメントのほかにも、記事のタイトルやタグなど、ほかの情報も必要ですよね。
そうなると、おそらくJSONは入れ子構造になるのではないかと思います。
たとえばこのように。

var articleData = [
  {
    articleTitle: "記事タイトル",
    articleUrl: "記事URL",
    commentData: [
      {author: "ユーザー1",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント1"
      },
      {author: "ユーザー2",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント2"
      },
      {author: "ユーザー3",
       authorImage: "https://source.unsplash.com/random",
       comment: "コメント3"
      }
    ]
  }
];

ということは、CommentListにdataを渡してあげるときは

//記事リストコンポーネント
var ArticleList = React.createClass({
  render: function() {
    return (
    <li>
      <article>
        <CommentList data={articleData[0].commentData} />
      </article>
    </li>
    );
  }
});

こうなっちゃうんですよね(´ヘ`;)ウーム…

これでも問題なく描画はできています。
でも…2記事め以降はどうしたら・・・?
ここでまさかJSONのデータ数を取得してforでまわす???

ということで、まだまだ調査&挑戦は続きます!

React使ってSPAを作るよ目次