CreateReactApp編(2)|React使ってSPAを作るよ

最終更新日


CreateReactApp編(1)|React使ってSPAを作るよの続きです。

CreateReactAppで構築したプロジェクトに既存のソースを移す

さっそく既存のソースを移します。
とりあえずcomposer.jsonとか/resourcesディレクトリとか、Laravel側で必要なファイルはそのままコピーしてきます。
さらにjsxファイルもsrcディレクトリに入れちゃいます。

create-react-appで作られたサンプルのソースは、.jsxではなく、.jsファイルですね。
ES6で書かれているようです。
つい先日TypeScriptの導入に挑戦したところなので、classの書き方はなんとなく見覚えがある感じです。

まずはエントリーポイントとなるIndex.jsを見ていきます。
(もとのプロジェクトではapp.jsxでしたがわかりやすいようにリネームしました。)

//Index.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import SiteHeader from './SiteHeader';
import ArticleArea from './ArticleArea';
import SiteFooter from './SiteFooter';

//メインコンポーネント
class Main extends Component {render() {
    return (
      <main>
        <ArticleArea />
      </main>
    );
  }
}

//bodyコンポーネント
class Body extends Component {render() {
    return (
      <div id="container">
        <SiteHeader />
        <Main />
        <SiteFooter />
      </div>
    );
  }
}

/* React + JSX */
ReactDOM.render(
  <Body />,
  document.getElementById('body')
);

まず、サンプルアプリに倣ってReactとReactDOMをimportします。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

Reactのところで{Component}をわざわざ指定していますね。
これはTypeScriptをやったときに型付きで

class Main extends React.Component<any,any>

としていたところが

こういうふうに書くようになっています。

class Main extends Component

う~ん。

var Main = React.createClass

class Main extends React.Component

class Main extends Component

ってな具合に書き方が変わってきていますね。

そして、ほかのjsファイルからコンポーネントをimportします。

import SiteHeader from './SiteHeader';
import ArticleArea from './ArticleArea';
import SiteFooter from './SiteFooter';

あとはほとんど変わりませんね。
render()の中身はこれまでJSXで書いてきたのとほぼ同じように書けます。

ES6で書くときに気を付けないといけないこと

Index.jsは簡単に済みましたが、ほかのファイルはそう簡単にはいきません。
たとえば、Tag.jsですが…
こういったコンポーネントがあります。

//タグ
class Tag extends Component {
  constructor(props) {
    super(props)
    this.handleAjax = this.handleAjax.bind(this);
  }
  handleAjax() {
    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() {
    return (
      <li><a ref="tag" name={this.props.id} onClick={this.handleAjax}>{this.props.tag}</a></li>
    )
  }
}

パッと見大きく異なるのは、constructorですね。
TypeScriptのときにもハマったように、onClickのイベントについて、constructorでthisをbindする必要があります。

あとは、関数のところの書き方も、これまで

handleAjax: function(){

となっていたところが、

handleAjax(){

だけでいけるようになりました。
閉じ括弧のあとのカンマも不要です。

それからもうひとつ。
これまでJSONで取得してきたデータをリストコンポーネントに渡すところで、データを格納するdataの初期化をgetInitialStateで行っていました。
それも、constructorで指定することになります。

//記事リストコンポーネント
export default class ArticleArea extends Component {
  //JSONデータ取得
  constructor(props) {
    super(props)
    this.state = {
      data: []
    };
    this.fetchURL = this.fetchURL.bind(this);
    this.loadArticleFromServer = this.loadArticleFromServer.bind(this);
    this.componentDidMount = this.componentDidMount.bind(this);
  }

//略

  loadArticleFromServer() {
    var t = this;
    $.ajax({
      url: apiURL,
      dataType: 'json',
      cache: false,
      timeout: 10000
    }).done(function(data) {
      t.setState({data: data});

//略

  render() {
    var articleNodes = this.state.data.map(function(article) {

//略

・・・といった修正を全体にかけたらできあがりです!

GitHubにソースあげておきました。
https://github.com/mayo31/minami-create-react-app

開発環境の依存関係がだいぶスッキリしますし、特に問題なさそうだったら、これをmasterにして進めていこうかな~と思いました。