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

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

React使ってSPAを作るよ目次

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

タグでの絞り込みも、コンポーネントの再利用で可能に

もう気づいた方もいるかもしれません。
前回コンポーネントを共通化して、別のデータを表示することができました。
ということは…
特定のタグで絞り込んだデータがあれば、それも同じ記事リストのパーツを使ってレンダリングできるということです。

そこで、「/api/article?tag=1」のようにtagのidを指定するとそのタグが設定されている記事のみのリストを返すようなURLを用意しておきます。
JSONの静的ファイルで持っていてもいいですが、私は同僚に作ってもらったAPIを叩いてます。

React使ってSPAを作るよ(13)で、記事リストに変更があったら自動で再レンダリングするという機能をつけました。
このとき$.ajaxは

    $.ajax({
      url: './api/article',
      dataType: 'json',
      cache: false,
    }).done(function(data) {
 //略

となっていました。
ということは、このurlに指定している値を変えてやれば、そこで返ってきたデータでレンダリングできるということです。

まずはこのURLを変数に格納しておきましょう。
すべてのJSXファイルで使えるように、bundle.jsの先頭に来るように書いておくといいです。
私の場合はとりあえず、bundle.jsより前に読み込むcommon.jsに書いておきました。

var apiURL = './api/article';

記事リストのコンポーネント全文はこちら。
urlのところだけ、変数のapiURLに変えました。

//ArticleArea.jsx

var React = require('react');
var TagList = require('./Tag.jsx');

//記事コンポーネント
var ArticleList = React.createClass({
  render: function() {
    var articleImage = {
      backgroundImage : "url(" + this.props.articleImage + ")" 
    };
    return (
    <li>
      <article>
        <a href={this.props.articleUrl} target="_blank" style={articleImage}>
          <h2>{this.props.articleTitle}</h2>
          <p>{this.props.articleDescription}</p>
        </a>
        <TagList data={this.props.articleTag} />
      </article>
    </li>
    );
  }
});

//記事リストコンポーネント
var ArticleArea = React.createClass({

  //JSONデータ取得
  getInitialState: function() {
    return {data: []};
  },
  loadArticleFromServer: function() {
    var t = this;
    $.ajax({
      url: apiURL,
      dataType: 'json',
      cache: false,
    }).done(function(data) {
      t.setState({data: data});
    }).fail(function(xhr, status, err) {
      console.error(apiURL, status, err.toString());
    });
    $('#spinner').css('display', 'none');
  },
  componentDidMount: function() {
    this.loadArticleFromServer();
    setInterval(this.loadArticleFromServer, 2000);
  },
  
  render: function() {
    var articleNodes = this.state.data.map(function(article) {
      return (
        <ArticleList articleTitle={article.articleTitle} articleUrl={article.articleUrl} articleDescription={article.articleDescription} articleImage={article.articleImage} articleTag={article.tagData} key={article.articleID}/>
      )
    });
    return (
    <ul id="article-list">
      {articleNodes}
    </ul>
    )
  }
});

module.exports = ArticleArea;

次に、タグをクリックしたときにこの変数にURLを代入する処理を作らないといけないですね。

前回作ったTag.jsxを修正しましょう。

//Tag.jsx

var React = require('react');

//タグ
var Tag = React.createClass({
  handleAjax: function() {
    apiURL = './api/article?tag=' + this.refs.tag.name;
    $('.overlay').removeClass('overlay');
    $('#overlay').css('display', 'none');
    $('#overlay-close').css('display', 'none');
    $('#spinner').css('display', 'block');
  },
  render: function() {
    return (
      <li><a ref="tag" name={this.props.id} onClick={this.handleAjax}>{this.props.tag}</a></li>
    )
  }
});

//タグリスト
var TagList = React.createClass({
  render: function() {
    var tagNodes = this.props.data.map(function(tag) {
      return (
        <Tag tag={tag.name} id={tag.id} key={tag.id}/>
      )
    });
    return (
      <ul className="tag-list">
        {tagNodes}
      </ul>
    )
  }
});

module.exports = TagList;

タグのli要素に、onClick={this.handleAjax}を追加しておきます。
このhandleAjax()関数の中でapiURLを設定するのですが、どのタグをクリックしたのか、タグのidを渡さないといけません。

フォームのときにもやったように、ref属性をつけることで要素を特定します。
そして、name属性にタグのidを入れておいて、this.refs.tag.nameでURLにくっつけています。

本当はdata-tagとかのカスタム属性にしておきたかったんですが、.dataTagとか、.data(‘tag’)とかしても渡せなくて、もちろん.data-tagもダメで、悩んだ末name属性を使いました…
誰かやり方教えてください(;´Д`)

こんな感じに動きます!

・・・

これ、履歴がないんですよね。
理想としては、HTML5のHistoryAPIを使って各タグのURLを持たせたりしたいですね。react-routerを使う方法などいろいろ調べてはいるのですが…
まだよく理解できません(;´Д`)

React使ってSPAを作るよ目次