LoginSignup
29
31

More than 5 years have passed since last update.

React.jsチュートリアルをcoffee-reactで書いてみる

Last updated at Posted at 2015-06-06

【2016年6月12日更新】0.15.0系になっている64a6dfca72aca4d13ab4cbf23c50eba8b9fabe8a(2016-06-08 23:09)からのフォーク版に差し替えました。それに合わせて古い情報などは更新しています。

React Tutorialのサンプルコードをcoffee-reactで書き直してみました。英語が苦手って方は、優秀な翻訳React.jsチュートリアル【日本語翻訳】をみてみてください。

チュートリアルのコードはgithub:reactjs/react-tutorialから入手できます。まずはそれを入手してください。

index.htmlを書き換える

pubilc/index.htmlを次のように書き換えます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>React Tutorial</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/base.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.6.2/remarkable.min.js"></script>
    <script src="https://wzrd.in/standalone/coffee-react-browser"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/cjsx" src="scripts/example.cjsx"></script>
    <script type="text/cjsx">
      # To get started with this tutorial running your own code, simply remove
      # the script tag loading scripts/example.js and start writing code here.
    </script>
  </body>
</html>

変わった所は.../browser.js.../coffee-react-browserになったところと、bodyにあるscriptタグの部分です。.../coffee-react-browserがCoffeeScript版JSX(CJSX)を変換するライブラリです。普通のJSXは不要なので、.../browser.jsが削除されています。

つぎに、bodyのscriptタグですが、次の通りです。

  • text/babel -> text/cjsx BabelによるES6なJSXからCJSXに変更しています。
  • scripts/example.js -> scripts/example.cjsx 拡張子を変更しています。
  • // -> # サンプルのコメント部分をCoffeeScriptの方式に変更します。

実際の本番はCJSXをcoffee-reactで普通のJavaScriptに変換しておくことを推奨します。なお、JSXの場合も普通のJavaScriptに変換しておくことが推奨されています。

example.jsをexample.cjsxに置き換える

scripts/example.jsを下記のscripts/example.cjsxに置き換えます。コードでやってることは全く一緒です。

example.cjsx
###
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only. Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###

class Comment extends React.Component
  rawMarkup: ->
    md = new Remarkable()
    rawMarkup = md.render @props.children.toString()
    { __html: rawMarkup }

  render: ->
    <div className="comment">
      <h2 className="commentAuthor">
        {@props.author}
      </h2>
      <span dangerouslySetInnerHTML={@rawMarkup()} />
    </div>

class CommentBox extends React.Component
  loadCommentsFromServer: =>
    $.ajax
      url: @props.url
      dataType: 'json'
      cache: false
      success: (data) =>
        @setState {data: data}
      error: (xhr, status, err) =>
        console.error @props.url, status, err.toString()

  handleCommentSubmit: (comment) =>
    comments = @state.data
    # Optimistically set an id on the new comment. It will be replaced by an
    # id generated by the server. In a production application you would likely
    # not use Date.now() for this and would have a more robust system in place.
    comment.id = Date.now()
    newComments = comments.concat [comment]
    @setState data: newComments
    $.ajax
      url: @props.url
      dataType: 'json'
      type: 'POST'
      data: comment
      success: (data) =>
        @setState {data: data}
      error: (xhr, status, err) =>
        @setState data: comments
        console.error @props.url, status, err.toString()

  constructor: ->
    super
    @state = data: []

  componentDidMount: ->
    @loadCommentsFromServer()
    setInterval @loadCommentsFromServer, @props.pollInterval

  render: ->
    <div className="commentBox">
      <h1>Comments</h1>
      <CommentList data={@state.data} />
      <CommentForm onCommentSubmit={@handleCommentSubmit} />
    </div>

class CommentList extends React.Component
  render: ->
    commentNodes = @props.data.map (comment) ->
      <Comment author={comment.author} key={comment.id}>
        {comment.text}
      </Comment>
    <div className="commentList">
      {commentNodes}
    </div>

class CommentForm extends React.Component
  constructor: ->
    super
    @state = author: '', text: ''

  handleAuthorChange: (e) =>
    @setState author: e.target.value

  handleTextChange: (e) =>
    @setState text: e.target.value

  handleSubmit: (e) =>
    e.preventDefault()
    author = @state.author.trim()
    text = @state.text.trim()
    return if !text or !author
    @props.onCommentSubmit author: author, text: text
    @setState author: '', text: ''

  render: ->
    <form className="commentForm" onSubmit={@handleSubmit}>
      <input
        type="text"
        placeholder="Your name"
        value={@state.author}
        onChange={@handleAuthorChange}
      />
      <input
        type="text"
        placeholder="Say something..."
        value={@state.text}
        onChange={@handleTextChange}
      />
      <input type="submit" value="Post" />
    </form>

ReactDOM.render(
  <CommentBox url="/api/comments" pollInterval={2000} />,
  document.getElementById('content')
)

どうですか?大分すっきりしたと思いませんか?さて、解説していきたいと思います。

classベースなReact

React 0.13.0以降はclassとして書けるようになりました。React.createClass(...)と書くよりもすっきりと意味論が通った形で書けるようになっています。これはES6だけで無くCoffeeScriptやTypeScriptもサポートされており、これからの標準になると思います。

初期化はconstructor()

stateの初期化はgetInitialStateではなくconstructor()で行います。他にも、getDefaultPropsの代わりに@defaultProps クラス変数propTypes@propTypes クラス変数を使います。例外はこの三つだけで、render等はインスタンスメソッドとして定義し、setState()等もインスタンスメソッドとして定義されています。なお、プロパティを使えるようになるために、constrauctor()で最初にsuper(引数指定は不要)を呼び出しておく必要があります。

ハンドルなメソッドはファットアロー=>を使え

->ではなく=>で定義が書いてある部分があるのがわかりますでしょうか?具体的にはloadCommentsFromServer()handleCommentSubmit()handleSubmit()です。最初の一つはちょっと特殊ですけど、他の二つはイベント発生時に呼び出されます。しかし、呼び出し時の@(this)は呼び出し元になってしまい、そのままでは@が呼び出し先のインスタンスでは無くなってしまいます。そこでファットアロー=>の出番となっています。他にも.bind(@)をつけて束縛する等の方法もありますが、CoffeeScriptでは=>を使った方がすっきり書けます。

この方法は$.ajax()でのsuccesserrorの処理でも使用しています。

その他

classベースなReactについてはReusable Components | React#ES6 Classesを参考にしてみてください。上のコードのライセンスはオリジナルと同じです。

書き終わった後に気付いたんですが、すでに書いている人がいました。Reactの公式チュートリアルをCoffeeScriptで書いてみた - Qiitaも見てみてください(ちょっとやり方違うので比較もできるかと)。

なお、上の記事ではエディタにmodeがなさそうっていってますが、Atomならlanguage-cjsxが、Sublime Textならsublime-cjsx1が既にあります。他のエディタは知らないです。


  1. 情報提供 thanks: @59naga さん 

29
31
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
31