話題かもしれないReactを使って簡単なアプリケーションを作ってみた。


数年前からjavascriptのフレームワークが数多く出てきたけど、その中でも最近話題かもしれないreactについて勉強してみた。
Facebook製で、Facebook、Instagram、GitHub (Atom) という大御所たちに使われたりしている。

作ったアプリケーション

Qiitaのタグを取得してタグごとに記事一覧を表示するもの。
トップは新着記事一覧を表示。

デモ

トレンド

Googleのトレンドで見てみるとじわじわと人気が出てきているのがわかる。
backboneはいわずもがな、angularも直近だけ見ると落ち込んでいるように見えるが、reactはじわじわと伸びている。
https://www.google.co.jp/trends/explore#q=react.js%2C%20backbone.js%2C%20angular.js%2C%20vue.js&cmpt=q

Github

大雑把な説明

使ったもの

jsのフレームワークはMVCじゃなくてMVWとか色々あるけどreactはVだけに特化したもの。
なので他のフレームワークと容易に共存できる。

ただ、今から紹介するテストではそんなめんどくさいことはせずにjQueryのajaxでAPIをとってきて、それをReactを使ってレンダリングしている感じ。
ルーティングはreact-routerというライブラリを使用した。
なので一応reactのみで動いていると言ってもいいでしょう。

使ってみた所感として、angularとは違って最初はとっかかりにくいけど、慣れると使いやすくなってくる。

あと別に使わなくてもいいみたいだけどJSXを使うのを推奨しているみたいなので使ってみた。これも慣れればすぐに使えるはず。

ドキュメントが英語しかないので、せっかくだし日本語にしてまとめていきたいなーと思ったけど、めんどくさくなったのでソースコードをそのまま貼り付ける。

ここなんやねんっていうのがあればコメントいただければ分かる範囲で答えます。

クロスオリジン

qiitaのAPIを叩くとき、たまにクロスオリジンで怒られるのはなんでだろうか。
怒られる時と怒られない時があるのは解せない。
あとiframeで記事詳細を埋め込もうとしたけど、これもクロスオリジンで怒られたので断念。
なくなく記事詳細は別タブで開くようにした。

ソースコード

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello React</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/index.css" />
  </head>
  <body>
    <div id="content"></div>
  </body>
</html>
<script src="scripts/libs/react-0.11.2.js"></script>
<script src="scripts/libs/JSXTransformer-0.11.2.js"></script>  
<script src="scripts/libs/react-router.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="scripts/app.js" type="text/jsx"></script>
javascript
/** @jsx React.DOM */

var Router = ReactRouter;
var Route = ReactRouter.Route;
var Routes = ReactRouter.Routes;
var DefaultRoute = ReactRouter.DefaultRoute;
var Link = Router.Link;

var HOST = 'https://qiita.com/api/v1';
var http = {
  items: function() {
    return $.ajax({
      url: HOST+'/items',
      dataType: 'json'
    });
  },
  itemsIntag: function(tag) {
    return $.ajax({
      url: HOST+'/tags/'+tag+'/items',
      dataType: 'json'
    });
  },
  tags: function() {
    return $.ajax({
      url: HOST+'/tags',
      dataType: 'json'
    });
  }
};

var Main = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    var that = this;
    if (this.props.params.tagId !== undefined) {
      http.itemsIntag(this.props.params.tagName).done(function(data) {
        that.setState({data: data});
      });
    } else {
      http.items().done(function(data) {
        that.setState({data: data});
      });
    }
  },
  componentWillReceiveProps: function(nextProps) {
    var that = this;
    if (nextProps.params.tagName !== undefined) {
      http.itemsIntag(nextProps.params.tagName).done(function(data) {
        that.setState({data: data});
      });
    } else {
      http.items().done(function(data) {
        that.setState({data: data});
      });
    }
  },
  render: function() {
    var work = this.state.data.map(function(work) {
      return (
        <List key={work['id']} title={work['title']}
               category={work['Category']}
                url={work['url']}
                image={work['user']['profile_image_url']}>
        </List>
      );
    });
    return (
      <section className="main">
        <ul>
          {work}
        </ul>
      </section>
    );
  }
});

var List = React.createClass({
  render: function() {
    return (
      <li className="list">
        <a href={this.props.url} target="_blank">
          <span ref="img" className="image">
            <img src={this.props.image} width="80"/>
          </span>
          <span className="name">
            <p className="title">{this.props.title}</p>
          </span>
        </a>
      </li>
    );
  }
});

var About = React.createClass({
  render: function() {
    return (
      <section className="about">
        <h1 className="bold">React</h1>
        <p>React is a JavaScript library for creating user interfaces by Facebook and Instagram.</p>
        <br/>
        <p>Many people choose to think of React as the V in MVC.</p>
        <br/>
        <p>Simply express how your app should look at any given point in time,</p>
        <p>and React will automatically manage all UI updates when your underlying data changes.</p>
        <br/>
        <p>When the data changes, React conceptually hits the refresh button, and knows to only update the changed parts.</p>
        <br/>
        <p><a href="http://facebook.github.io/react/">{'http://facebook.github.io/react/'}</a></p>
      </section>
    );
  }
});

var Menu = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    var that = this;
    http.tags().done(function(data) {
      that.setState({data: data});
    });
  },
  render: function() {
    var tags = this.state.data.map(function(tag) {
      return (
        <li key={tag['name']}><Link to="tag" params={{tagName: tag['name']}}>{tag['name']}</Link></li>
      );
    });
    return (
      <ul>
        <li><Link to="about">{'What\u0027s React?'}</Link></li>
        <span>TAGS</span>
        {tags}
      </ul>
    );
  }
});

var App = React.createClass({  
  render: function() { 
    return (
      <div className="container">
      <header>
        <h1><a href="#" className="title">REACTJS TEST</a></h1>
        <Menu />
      </header>
      <this.props.activeRouteHandler/>
      </div>
    );
  }
});

var routes = (
  <Routes>
    <Route name="app" path="/" handler={App}>
      <Route name="about" handler={About}/>
      <Route name="tag" path=":tagName" handler={Main} />
      <DefaultRoute handler={Main}/>
    </Route>
  </Routes>
);

React.renderComponent(routes, document.getElementById('content'));