今週の進捗:React.js

さて、NodeSchoolで基礎力もアップしたことだし、今週末も引き続き嫁そっちのけでReact.jsで作ったサイトをいじくり回した。
前の記事を書いてから進歩した点

browserifyを使って依存関係を解決!

ひとつのjsファイルにまとめてそいつをuglifyjsで難読化した。
サーバサイドレンダリングを導入するまでには至らず。

browserifyはこうやっている。

browserify -t reactify  public/src/app.jsx | uglifyjs > public/build/bundle.js

browserify reactify とかで検索すると、いきなりgulpfile.jsのサンプルを載せてる記事がたくさんヒットするんだけど、そんないきなりgulpなんて使いこなせないし。。
ので、まずは自分のjsファイルにひとつずつbrowserifyコマンドを適用して吐かれたエラーを見て対処した。コマンドの安心感。
しかしgulpもbrowserifyも、エラーメッセージが不親切だった。
toUpperCase is not a function ってコンソールに出て、30分悩んだらコンポーネントをmodule.exportsしていなくて要するにぬるぽになっていただけだったとか、そんなのばっかりだった。
あと結構ウェブで拾ったpackage.jsonをコピペしちゃうんだけど、バージョンが古かったりして依存関係おかしくなるので、自分でnpm install --save hogeとかしたほうがいいんだと思う。

ページングを実装した!

ライブラリも幾つかあったんだけど、どっちみち学習コストありそうだったので自分で作った。
クロージャの変数スコープについてよくわかってなくて焦った。

そんなところかな

なんかやってることがしょぼい!笑
まあこんな調子でのほほんとやっていきたいと思います。

NodeSchool International Day in Tokyoに参加してきた

NodeSchool International Day in Tokyo

諸事情で途中からの参加になってしまったけど、みんなモクモクと作業していたので問題なく溶け込めた。
functional javascript workshopは前にちょっとやっていたんだけど、今回はjavascripting とかlearnyounodeとかちょこっとかじった。
基礎を学ぶのって意識しないとなかなかやらないから、いい機会だった。
ありがたい。

ちょっとLTで前の記事に書いたことを喋らせてもらったんだけど、準備が間に合わずぐだぐだになってしまった。
5分を甘く見ていたせいで、自己紹介だけでほぼ終わった。申し訳ない。
それでも懇親会では周りから反応をいただけたので収穫ありだった。
やはりアウトプットは大事だと思う。

その他の所感としては、

  • 小学生くらいの小さい子が来ていて、「セミコロン?」とか言ってるのがめちゃくちゃかわいかった
  • オープンな雰囲気でよかった。暖かい日だったし、夏期講習みたいな気分だった
  • マークシティの構造がわけわかんない
  • そういえば世界各地の会場ともつながっていたみたい。世界規模でやってるなんて、すごい話。

といったところ。
次回は夏らしいのでまたまったり参加したい。

GWなのでReact.jsでイベントサイトを作った

http://jam-navi.herokuapp.com/

イベントといっても音楽関係でしかもジャムセッション縛りという割とニッチなサイトを作った。

ざっくり

とりあえずリリースすることが最優先だったのでサーバサイドレンダリングとかはなし。Fluxとかも知らない。けど、「投稿したものがタイムラインで流れてくる」というのはReact.jsと相性が良さそうだと直感して使ってみた。
ルーティングはreact-router使ってる。が、ログイン画面からホーム画面への遷移とかは window.location.href = "/" みたいにして全画面読み込みしてる。子供から子供への遷移がうまくいかなかった。
サーバサイドはNode.jsで、バックエンドとしてParseを使っている。
Node.jsでのアプリは2つ目で、ちょっとずつ慣れてきたところ。

Javascript書いててハマったポイント1: this

みんな言ってるけど、thisがすぐ迷子になる。
あーハイハイって言ってbindすればいいんだけど、thisよ。。という気持になる。
これはRegister Success! って1度出して3秒後に(突如)消す、というコード。 .bind(this) がないとsetStateなんてfunctionはねえ!と怒られる。 セミコロンはなくてもいいんダネ!

    var successDialog
    if (this.state.postSuccess) {
      successDialog = this.getPanelInfo("Register Success!")
      var timeID = setInterval(function() {
          this.setState({postSuccess:false})
          clearInterval(timeID)
        }.bind(this), 3000)
    }

Javascript 書いててハマったポイント2: 依存関係の解決方法

「よーし、サーバサイド脳だ!」という頭で var hoge = require('hoge') などと書いたらブラウザで動かなかった。
requireはブラウザでは使えないんだって。どうやら、browserify とかいうのをかませればrequireで解決した依存関係をなんとかしてくれるみたいだ。

結局今回は親のhtmlに<script>を貼ってスコープ内にクラスを展開したけど、これって全クラスに対してフルオープンになってしまっていて微妙なんだと思う。
それとも、browserify使っても一緒なのだろうか。ちゃんと調べ切れていない。

React.js でハマったポイント1: jsxの記法

例えばclass属性を指定したい時、このように書かなければならない。

<div className="col-lg-6">

そしてstyle属性を指定したい時、このように書かなければならない。

<div className="panel-heading" style={{float:'right'}}>

jsx記法でclass="hoge" と書くと変換する時に無視される。
「ここはこのソースコードコピペでいけるはず!よし!」とかやってたらうまくスタイルが適用されなくて実はclass属性がjsxに無視されてたとか何度かやった。

もちろん、React.jsの生のソースを書いてもいいわけだが、このあたりhtmlと混同するし、例えばデザイナーとの分業みたいなことも難しいというか無理だと思った。

まあ弊社でデザイナーさんとコードレベルで連携することはこれまでなかったので個人的には別にいいのだけど。

一方例えばロジックをhtmlに埋め込むみたいなことは直感的にできたので、これは使いやすいなと思った。

<div className="hoge">
  <foo />
  {successDialog ? successDialog : null}
</div>

このようにsuccessDialogに値がある時だけタグを吐き出す、という書き方ができる。
これは別にこうしろとどこかに書いてあったわけではないけど、ちょっとしたダイアログの出しわけみたいのはこれでいいやと思ってやってる。

まとめ

Reactっぽいのはイベントの登録と取得の部分。
2秒ごとにpollingしてるので、イベントがどこかから登録されると自動的に一覧に反映される。
ページングとかまだ入れてないのでほんとまだよちよち歩き状態。

一覧と登録の機能だけなので、ほとんどチュートリアルを見ながら作れたけど、React.js は思ったよりは学習にコストがかかることがわかった。
どこかの記事でRiot.js 簡単でいいよとか書いてあった。

ちなみにParseの無料枠には 1秒間に30リクエストまで という制限がある。
2秒に1回pollingしてるということは、例えば60個くらい同時に窓を立ち上げられたらこちらはお手上げである。
読者諸君の良識ある行動に期待している。

javascriptとSwiftでArray#reduceを自分でも実装してみた話

最近1年くらいQiitaにばかり書いていましたが、こちらではもう少し日記みたいな感じで徒然と書いていこうと思います。

さて、今日はちょっと趣味でjavascriptの勉強をしようと思いました。
これまでjsは真面目に書いてこなかったのですが、とりあえずとっかかりにと思ってfunctional-javascript-workshopというのをやってみています。

Functional Javascript

まだ18 challenges中7個目ですが、既にreduceを再帰で実装しましょうという課題が出てきました。
こういうのやってると新人研修を思い出します。
私の場合回答はこんな感じになりました。

/* @flow */
module.exports = function reduce(arr:Array<any>, fn:any, initial:any):any {
  var reduceOneFn:any = function reduceOne(index:number, value:any):any {
    if (index > arr.length - 1) return value
    return reduceOne(index + 1, fn(value, arr[index], index, arr))
  }
  return reduceOneFn(0, initial)
}

せっかくなのでFlowtypeで型チェックをしながらやっています。ちなみにGitHubが出しているatomというエディタだとFlowtypeのpackageをいれればリアルタイムで型チェックをさせられるので便利でした。
上の例だとanyばっかりであまり型チェックが意味をなさなくなってますが。。
ジェネリクスを使ってFlowtypeに型チェックをさせようとするとなんだかうまくいかなかったんです。

なんだかモヤッとしたので、型に厳格な例の言語でやるとどんな感じになるのか、やってみました。

Swift

extension Array {
  func reduceAFn<U>(index:Int, value:U, combine:((U, T, Int) -> U)) -> U {
    if index > self.count - 1 { return value }
    return reduceAFn(index + 1, value: combine(value, self[index], index), combine: combine)
  }
  func reduceA<U>(combine:((U, T, Int) -> U), initial:U) -> U {
    return reduceAFn(0, value: initial, combine: combine)
  }
}

ジェネリクス使えるよ。そう、Swiftならね。

SwiftのArrayの宣言部分ではすでに型引数 T が宣言されているので、extension内でもそのまま使うことができました。
他のところは自分で U という型引数を定義しています。
なおSwiftではローカル関数内でローカル関数自身を呼ぶことができないという制限があり、reduceAFnを外だしせざるを得ませんでした。

ユースケースとしてはこんな感じですね。

// 配列の中身を全部足す
var r = [0,1,2,3,4,5].reduceA({(prev, current, index) in return prev + current}, initial: 0)
println(r) // 15

// 単語の出現回数を数える
var r1:[String:Int] = ["hello", "hello", "yellow", "yellow", "yellow", "foobar"].reduceA({(prev, current, index) in
  var countMap:[String:Int] = prevv
  countMap[current] = (countMap[current] ?? 0) + 1
  return countMap
}, initial: [String:Int]())
println(r1) // [hello:2, yellow:3, foobar:1]

再帰的な書き方って、人に説明するのもされて理解するのも難しいんですが、問題が解けたときの満足感はなにものにも代えがたいものがありますね。

ひとまず眠いし今日はこんなもんで。