Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ReactをCoffeeScript2で書くためのアレやソレ

More than 1 year has passed since last update.

ReactチュートリアルをCoffeeScriptで書こうとして四苦八苦、試行錯誤したしている記録

開発環境

まずは、ここから、Reactチュートリアルをクローンして、 react-tutorial ディレクトリに入ります。

$ git clone https://github.com/reactjs/react-tutorial.git
$ cd react-tutorial

そして、必要なパッケージをインストールします。

$ npm i --save coffee-react
$ npm i --save node-dev

node.jsと、npmは最新版にしておいてくださいね。
あと、 ./node_modules/.bin にPATHを通しておくと便利です(というか通ってないと動きません)。
デバッグ用のnodeモジュールも入れておきました(後で使います)。
そして、別ターミナルでコンパイル監視とアプリケーションサーバーを起動します。

$ cjsx -wc public/scripts

監視モードに入ると、そのコンソールが使えなくなるので、別コンソールで、

$ node-dev server.js

さらに別のコンソールで react-tutorial/public/index.html を開き、サンプルの削除と、スクリプトの種類を変更します。

    <script type="text/babel" src="scripts/example.js"></script>
    <script type="text/babel">

の、 example.js の行を削除(いきなりチュートリアルをやるため)。
そして、その下のscript行を、

    <script type="text/cjsx">

と修正。
JSXのCoffeeScript版で記述することを高らかに宣言します。
本来であれば、これで、 script タグの間にCJSXをゴリゴリ書いていくとReactアプリケーションが作れます。
…と言いたいところですが、いろいろハマリどころが多いので書いていきます。

ReactをCoffeeScriptで書くということ

そもそもReactはJSXというシンタックスシュガーを用いて記述しますが、それをこれまたシンタックスシュガーであるCoffeeScriptで書こうというのがそもそも間違いの始まり。

Reactチュートリアルの、

var CommentBox = React.createClass({
 render: function() {
   return {
     <div className="commentBox">
       Hello World!.
     </div>
   };
 }
});
ReacDOM.render(
  <CommentBox />,
  document.getElementById('content')
);

これを、CoffeeScript(正確にはCJSX)に直すと下記のようになる。

CommentBox = React.createClass
  render: ->
    return <div className="commentBox">
      Hello World.
    </div>

ReactDOM.render <CommentBox />,
  document.getElementById("content")

ここに辿り着くまでにかなり長い旅をした(遠い目)。
これのハマリポイントとしては、まずはインデント。

    return
      <div className="commentBox">
        Hello World.
      </div>

普通にインデントでこう書くとコンパイラに怒られる。なんでやねん。

    return (
      <div className="commentBox">
        Hello World.
      </div>
    )

しかし、こうするとコンパイルは通ってちゃんと動く。
しかしCoffeeScript的に美しくない。
なので、最初の正解例のように、

    return <div className="commentBox">
      Hello World.
    </div>

こうする。
これで、ちゃんとコンパイルも通って実行出来る。
return で始まって div で終わってるのでちょっと気持ち悪いが、Reactはひとつのコンポーネントでは親要素をひとつしか返せないのでこれでヨシとする。

人様のチュートリアルをやってみる

こういう時は自称「初心者」の方のblogが役にたつ。ポエムだとただのメモなので環境などが推測しづらい。というわけでこの方のこれをやってみる。

React入門チュートリアル

これをCJSXで書いてみる。
まずは、これ

const TodoList = () => {
  return (
    <ul>
      <li>ほげ</li>
      <li>ほげ</li>
      <li>ほげ</li>
    </ul>
  );
}

ReactDOM.render(
  <TodoList />,
  document.getElementById('content')
);

これを、

TodoList = =>
  return <ul>
    <li>ほげ</li>
    <li>ほげ</li>
    <li>ほげ</li>
  </ul>

  ReactDOM.render <TodoList />,
    document.getElementById('content')

こう書いた。ハイ、察しのいい方は気づきましたね。ブラウザになにも出てこないんですね。バベってみたり、CoffeeScript2でコンパイルしてからバベってみたり、いろいろしたんですけどダメなんです。

そうなんです、インデント間違ってました。

これでは、最初の render が実行されません。
というわけで、

TodoList = =>
  return <ul>
    <li>ほげ</li>
    <li>ほげ</li>
    <li>ほげ</li>
  </ul>

ReactDOM.render <TodoList />,
  document.getElementById('content')

こうしたらちゃんと動きました。

初期生成されるコンポーネントを指定する

なんか、最初にレンダリングするところで初期コンポーネントを指定するのが汎用性が低く感じられて(←汎用厨)変数で指定するようにしてみた。

#============================================================================
# Todo List
#============================================================================
TodoList = =>
  return <ul>
    <li>ほげ</li>
    <li>ほげ</li>
    <li>ほげ</li>
  </ul>


#============================================================================
# Initial Component
#============================================================================
initComponent = TodoList

#============================================================================
# Display Base Component
#============================================================================
MainComponent = =>
  return React.createElement(initComponent, null)
ReactDOM.render <MainComponent />,
  document.getElementById('content')

いちおうコンパイルは通って動いている。「初期コンポーネントを変数に入れるのと、レンダリングするコンポーネントを指定するのは、同じ手間じゃね?」というツッコミはスルー。

………としたんですが、この後に TodoList に引数を渡さないといけなくなり、結局こうなりました。

#============================================================================
# Todo List
#============================================================================
TodoList = =>
  return <ul>
    <li>ほげ</li>
    <li>ほげ</li>
    <li>ほげ</li>
  </ul>

#============================================================================
# Display Base Component
#============================================================================
ReactDOM.render <TodoList />,
  document.getElementById('content')

元に戻りました……。

クラスを作ってみる

CoffeeScriptや、ES6の特徴である「クラス」が作れるので、クラスを作ってみます(元のチュートリアルで作ってるのでやってるだけ)。先頭に、TodoApp というクラスを定義します。コンストラクターで表示するメンバーを定義します。

#============================================================================
# TodoApp
#============================================================================
class TodoApp extends React.Component
  constructor:->
    super()
    @state = {items: ['ほげ', 'ほげ', 'ほげ']}

そして、render メソッドを定義します。

  render:->
    return <div>
      <h3>Todoアプリ</h3>
      <form>
        <input />
        <button>Add</button>
      </form>
      <TodoList items={@state.items} />
    </div>

フォームの下で、コンストラクターで定義した配列を TodoList に渡しています。
次に、TodoList を、渡された配列を表示するように改修します。

class TodoList extends React.Component
  constructor:(props)->
    super(props)
    @state = props

  render:->
      return <ul>
        {@state.items.map (item, index) ->
          <li key={index}>{item}</li>
        }
      </ul>

TodoAppTodoList を呼び出した際に、items というパラメータで渡している配列を、constructorprops という変数で受け取っています。super() を呼ぶのは決まりになってますね。そして、mutableな変数 state に代入しています(stateについて分からない方はReactをググってください)。
renderメソッドではconstructorで受け取った配列をmapでループ処理しています。@state.itemsの各要素を(item, index)という二つの配列で、要素と配列番号を取り出します。それをそのままタグで表示しています。ループで複数の要素を表示する時に、keyを設定しないとJSXコンパイルの時に怒られるらしいです(必ず指定しているので怒られたところを見たことがない)。
全部まとめるとこんな感じですね。

#============================================================================
# TodoApp
#============================================================================
class TodoApp extends React.Component
  constructor:->
    super()
    @state = {items: ['ほげ', 'ほげ', 'ほげ']}

  render:->
    return <div>
      <h3>Todoアプリ</h3>
      <form>
        <input />
        <button>Add</button>
      </form>
      <TodoList items={@state.items} />
    </div>

#============================================================================
# Todo List
#============================================================================
class TodoList extends React.Component
  constructor:(props)->
    super(props)
    @state = props

  render:->
      return <ul>
        {@state.items.map (item, index) ->
          <li key={index}>{item}</li>
        }
      </ul>

#============================================================================
# Display Base Component
#============================================================================
ReactDOM.render <TodoApp />,
  document.getElementById('content')

多分動くと思います。

次は、チュートリアルにもあるように、配列を空にしてユーザーの入力されたものを追加して、それをリアルタイムで表示する、という感じに改修してみたいと思います。


永遠につづく。

digitarhythm
いろいろ作っています。 フルスタックフレームワーク「ViewllerJS」 https://www.digitarhythm.net/viewllerjs ブラウザでWebゲーム開発「enforceIDE」 https://www.prominence.tv/enforceIDE WebブラウザでProcessing「InstaProc」 https://processing.fun/
http://www.digitarhythm.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away