8
8

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-07-10

前回の続きで、React Tutorialのサンプルコードを今度はLiveScriptで書き直してみました。やり方は前回と一緒なので、まだ見てない人はそっちも見てください。

index.htmlを書き換える

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

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello React</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/0.13.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/livescript/1.4.0/livescript-min.js"></script>
    <script src="http://www.preludels.com/prelude-browser-min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/ls" src="scripts/example.ls"></script>
    <script>
      require("LiveScript").go();
    </script>
  </body>
</html>

今回はlivescript-min.jsprelude-browser-min.jsが加わっています。また、MIMEタイプはtext/lsとなっています。最後のスクリプトコードはLiveScriptをJavaScriptに動的にコンパイルするのに必要です。

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

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

example.ls
/**
 * 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.
 */

require! 'prelude-ls': {Obj}

class Component extends React.Component implements React.DOM
    @element = ->
        React.create-factory(@) ...

class Comment extends Component
    render: ->
        @div class-name: \comment,
            @h2 class-name: \commentAuthor,
                @props.author
            @span dangerouslySetInnerHTML:
                __html: marked @props.children.to-string!, sanitize: true

class CommentBox extends Component
    (props) !->
        super ...
        @state = data: []

    access-comments: (settings) !->
        default-settings =
            url: @props.url
            data-type: \json
            success: (data) !~>
                @set-state data: data
            error: (xhr, status, err) !~>
                console.error @props.url, status, err.to-string!
        $.ajax default-settings with settings

    load-comments-from-server: !~>
        @access-comments cache: false

    handle-comment-submit: (comment) !~>
        @set-state data: @state.data ++ [comment], !->
            @access-comments type: \POST, data: comment

    component-did-mount: !->
        @load-comments-from-server!
        set-interval @load-comments-from-server, @props.poll-interval

    render: ->
        @div class-name: 'commentBox',
            @h1 null, 'Comments'
            CommentList.element data: @state.data
            CommentForm.element on-comment-submit: @handle-comment-submit

class CommentList extends Component
    render: ->
        @div class-name: \commentList,
            @props.data.map (comment, index) ->
                Comment.element author: comment.author, key: index,
                    comment.text

class CommentForm extends Component
    handle-submit: (e) !~>
        e.prevent-default!
        ref-nodes = @refs{author, text} |> Obj.map React.findDOMNode
        ref-vals = ref-nodes |> Obj.map (.value.trim!) |> Obj.compact
        if \author of ref-vals and \text of ref-vals
            @props.on-comment-submit ref-vals
            ref-nodes |> Obj.each -> it.value = ''

    render: ->
        @form class-name: \commentForm, on-submit: @handle-submit,
            @input type: \text, placeholder: 'Your name', ref: \author
            @input type: \text, placeholder: 'Say something...', ref: \text
            @input type: \submit, value: \Post

React.render CommentBox.element(url: 'comments.json', poll-interval: 2000),
    document.get-element-by-id \content

もう、なんとういうか、なにか別世界に来た感じです。さて、解説していきたいと思います。なお、classベースなどのcoffee-reactと被る部分は説明を省きます。

<タグ>からの解放

JSXおよびJSX派生言語(coffee-react等)で一番の特徴はHTMLと同じタグ表記です。LiveScriptにもreact-livescript-jsxというものがあるのですが、今回はつかっていません。SGMLからの伝統を引き継ぐこのタグ表記はHTMLだけしか知らない人にはいいかもしれませんが、いかんせん、冗長すぎて書くのがつらいです。HAML/Jade/Slimとかに慣れている人からすると、今更タグで書くのは苦痛です。

React.jsはJSXを使いたくない人にもちゃんとコーディングする方法を用意しています。新たにelementを作る方法は三つです。

  • React.createElement()を使う。
  • Recat.createFactory()を使う。
  • React.DOM.要素名()を使う。(HTML要素のみ)

最後はHTML要素のみにしか使えませんが、どれを使ってもいいです。ただ、どれも書き方が冗長です。何度も何度もReact...って書いていくのはつらいです。そこで、一工夫することにします。

HTML要素はReact.DOMをmixin!

例えば、div要素はReact.DOM.div()とすることで作ることが可能です。では、これをもっと簡単にスマートにするには?よくみるとdiv()React.DOMのクラスメソッドです。そう、だったらmixinしちゃえば、@div()とかけるようになるということです。LiveScriptではclass定義にimplementsキーワードでmixinが可能です。親クラスとは別に指定できますので、これを使いましょう。

ReactClassはRecat.createFactory()をクラスメソッドで実行!

残念ながらReactClassにはReact.DOMは使えません。同じ方法を取ろうにも、class定義時にあとどれだけReactClassを定義するかなんて知る方法はありません。同じ方法はあきらめて、簡単にFactoryを取得・実行するようにすればいいと考えました。各ReactClassでelemente()クラスメソッドを実装し、内部ではReact.createFactory()でFactoryの生成、そして、渡された引数を渡して実行すればいいのです。

まとめたものをクラスとして作って継承していく

こうして、上をまとめた最低限のクラスは下記になります。

class Component extends React.Component implements React.DOM
    @element = ->
        React.create-factory(@) ...

コンポーネント作成するときは、React.Componentの代わりに上のCompoentを継承するようにすると、render()@div()Comment.element()という表記ができるようになります。最終的には今回のコードのようなHAML/Jade/Slimっぽいコードで書くことができると言うことです。

なお、上の3行の部分はパブリックドメインですので、お使いになる場合はご自由に。

その他

キャメルケースをチェインケースにしたり、パイプ|>で繋いだり、prelude.lsを使ったり、LiveScriptっぽくできそうな所はなるべくしてみて、DRYの原則に立つようにしてみました。でも、そもそものコードが短いので、あまり関数型プログラミングっぽくできなかったです。

まとめ

いかがだったでしょうか?LiveScriptはCoffeeScriptをさらに強力にしたCocoに関数型プログラミングの要素を追加した、もう、なんでもありな言語に仕上がっていますので、すごくすっきりとしたコーディングが(たぶん)可能です。これを機に皆さんもLiveScriptを触ってみてください。次回はFlux Tutorial - Todo Listあたりに挑戦してみようかなと思っています。

8
8
0

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
8
8