GW Hackathon - Day3
数時間かけてfacebookログイン導入。URLスキームの設定ができてなくてはまる。 safariを起動するとシミュレーターがめちゃくちゃ重くなるのはなぜですか?
Add Parse + React Native
http://stackoverflow.com/questions/29453215/react-native-and-parse-facebookutils
Parse.User.current()が動かないので軽く死ぬ。 ライブラリを読むとlocalStorage使っててReact NativeにはWebじゃないので使えない。 結局、React NativeのAPI, AsyncStorageで自前実装。辛い。
Add Navigator to have to view with state
Navigatorの使用方法に悩む。結局はどこにsetStateを挟むかの問題。
詳しくはあとでブログに書きます。
GW Hackthon - Day2
May 3, 2015
昨日slackoverflowで聞いたのは朝起きたら回答がありました。すごく初歩的なことを聞いたようでとても恥ずかしいです^^; 数時間煮詰まってわからなければ、帰り際にstackoverflowに投稿すれば朝には回答があるかもしれないので、そうやって聞けることが収穫でした。リソースがないスタートアップでは大事ですね。
午前中はRefluxとaltのコードを読んで、入れてみるも動かないで何度もgit checkout .して悶々としてました。Reflux、altともに先週4000近いnpmダウンロードがあるので2トップなので選んだのですが、よく見てみるとfacebook fluxが15000ぐらいダウンロードされてて圧倒的でした。
しかも、githubにexampleでchatとtodoがあるじゃないですか。午後は気持ちを入れ直してFacebook Fluxと戦いました。結局5時間ぐらい戦ってようやくReact NativeのチュートリアルのコードをFlux化できました。
React NativeでFluxを使う
React NativeのMovieチュートリアルをFluxで実装します。 Flux実装が山ほどある中、Facebook Fluxのgithub上のexampleのtodo,chatで使われている本家のFluxモジュールです。Reflux, altが色々enhanceしたもののようですが、npmのダウンロード数をみると圧倒的にFluxがダウンロードされています。やはりFacebook押しだと安心感がありますね。
では、必要なnpmを入れていきます。
$ npm install flux $ npm install object-assign $ npm install keymirror
まず全てindex.ios.jsに書かれていたもの分解して配置していきます。 最終的には、下記のようなツリー構造になります。
./index.ios.js
./app
├── actions
│ └── MovieActions.js
├── app.js
├── components
│ └── MovieListView.js
├── constants
│ └── MovieConstants.js
├── dispacher
│ └── AppDispatcher.js
├── dispatcher
│ └── AppDispatcher.js
├── stores
│ └── MovieStore.js
└── utils
└── MovieWebAPIUtils.js
index.ios.jsはシンプルにapp/app.jsを呼ぶ作りです。
"use strict";
var React = require('react-native');
var {
AppRegistry
} = React;
var App = require('./app/app')
AppRegistry.registerComponent('App', () => App);
app/app.jsは、Viewを呼びます。今回はWebAPIを用いてMovieリストを取ってくるので、最初の一回目を取得するコードgetMovies()を入れておきます。これでActionを走らせることになります。
// app/app.js
var MovieListView = require('./components/MovieListView');
var MovieWebAPIUtils = require('./utils/MovieWebAPIUtils');
MovieWebAPIUtils.getMovies();
module.exports = MovieListView;
app/dispatcher/AppDispatcherは、DispatcherをFluxライブラリを用いて作ります。 dispatchはObserverパターンそのものですね。このパターンがわかってる人なら感覚的にわかると思います。
// app/dispatcher/AppDispatcher
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
app/utils/MovieWebAPIUtils.jsはAPIを定義して、Actionを発火します。今回はActionの発火はこれだけになりますが、アプリケーションを組んでいくとユーザイベントから発火させることがあるのは容易に想像できますね。
// app/utils/MovieWebAPIUtils.js
var MovieActions = require('../actions/MovieActions');
var REQUEST_URL
= 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
module.exports = {
getMovies: function() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
MovieActions.receiveMovies(responseData.movies);
})
.done();
}
};
app/actions/MovieActions.jsは、アクションの登録です。決められたconstantに基づいてDispatchします。
// /app/actions/MovieActions.js
'use strict';
var AppDispatcher = require('../dispatcher/AppDispatcher');
var MovieConstants = require('../constants/MovieConstants');
var MovieActions = {
receiveMovies: function(movies) {
AppDispatcher.dispatch({
actionType: MovieConstants.MOVIES_CREATE,
movies: movies
});
},
};
module.exports = MovieActions;
app/constants/MovieConstants.jsは、Contantsを定義してます。これらのConstantsを見てstoreは状態を管理していくことになります。
//app/constants/MovieConstants.js
var keyMirror = require('keymirror');
module.exports = keyMirror({
MOVIES_CREATE: null,
});
app/stores/MovieStore.jsは、MVCでいうModelに当たるビジネスロジックを含める肝になる部分です。 今回は難しいことはやっていなく、dipatch用のメソッドが生えてるのがわかります。
'use strict';
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var MovieConstants = require('../constants/MovieConstants');
var CHANGE_EVENT = 'change';
var _loaded = false;
var _movies = {};
var MovieStore = assign({}, EventEmitter.prototype, {
isLoaded: function() {
return _loaded;
},
getMovies: function(){
return _movies;
},
setMovie: function(movies){
_loaded = true;
_movies = movies;
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
/**
* @param {function} callback
*/
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
/**
* @param {function} callback
*/
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
// Register callback to handle all updates
AppDispatcher.register(function(action) {
switch(action.actionType) {
case MovieConstants.MOVIES_CREATE:
MovieStore.setMovie(action.movies);
MovieStore.emitChange();
break;
default:
// no op
}
});
module.exports = MovieStore;
最後にViewです。app/components/MovieListView.jsは、dispatchされるために登録します。 setStateで書き換えるのは変わりません。
"use strict";
var React = require('react-native');
var MovieStore = require('../stores/MovieStore');
var {
StyleSheet,
Image,
ListView,
Text,
View,
} = React;
var Chifan = React.createClass({
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false
};
},
componentDidMount: function() {
MovieStore.addChangeListener(this._onChange);
},
_onChange: function() {
this.setState(this.getMovieState());
},
getMovieState: function() {
var movies = MovieStore.getMovies();
return {
dataSource: this.state.dataSource.cloneWithRows(movies),
loaded: MovieStore.isLoaded()
};
},
render: function() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
);
},
renderLoadingView: function() {
return (
<View style={styles.container}>
<Text>
Loading movies...
</Text>
</View>
);
},
renderMovie: function(movie){
return (
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
},
});
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'blue',
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8,
textAlign: 'center',
},
year: {
textAlign: 'center',
},
thumbnail: {
width: 53,
height: 81,
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
listView: {
paddingTop: 20,
backgroundColor: '#F5FCFF',
},
});
module.exports = Chifan
以上ですが、見てもらうとわかると思いますが、Viewはほぼ変わりません。これはReactがView操作に特化してるからです。Fluxがやっていることは情報を1方向にすることが主です。あとはどこにビジネスロジックを入れるとか、コードの見通しがよくなることですね。
GW (self)Hackathon - Day1
May 2, 2015
今年のGolden Weekは長いので技術力のブラッシュアップのために一人ハッカソンを開始します。
ReactivecocoaとReactive Nativeのどちらをターゲットにしようか迷いましたが、React Nativeにすることにしました。理由はReactivecocoaのswift版がβ版でドキュメントがないので、初学者は絶対はまると思ったからです。React Nativeはまだまだβ版的要素が多いですが、先週少し触ったReactjsのようにこれから主流になりうる技術の習得ができると考えたからです。あとはReact Nativeの可能性を肌で感じたいと思ったからです。
またSublimeからAtomに乗り換え中なのでAtomも触りつつ進めてます。 Nuclide早く使いたい!
React Native
ドキュメントをやる。
Get Started section
ドキュメント通りで問題なし。エディタAtomにreactとjshintパッケージを入れました。
Tutorial
Fetchのところでもそうですが、ES6のArrow Functionが出てきて慣れてませんでした。
(row1, row2) => row1 !== row2,
これは
function(row1, row2){ return row1 !== row2 }
ですね。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
またListViewのdatasourceにcloneWithRowsなどが生えててドキュメントにないのもはまりそうです。とりあえず、こういうものだと思ってコーディングしていきます。ソースはgithubにありますね。Array<any>とか読めん。。
ES6 syntaxをみてみる
ES6の文法が普通に出てきます。下記のサイトでFeatureを復習しておきましょう。
https://github.com/lukehoban/es6features
React native + Flux
明らかにindex.ios.jsに全てを書こうとすると破綻しそうです。 awsome-react-nativeにあるgithub repoを眺めているとAppディレクトリで階層を作って入れています。その中身はFluxのcomponentsになってるようにも思います。
やってみないとわからないので、とりあえずやってみることにします。 ざっと見た感じで下記のレポジトリが良さげなので真似します。
https://github.com/mikkoj/NortalTechDay
分解していくとはできたのですが、watchをナイーブに実装していて初学者の私には少し辛いです。 なので、Flux implementationを調べるとたくさんあります。この世はFlux実装戦国時代ですね。
その中でもalt(alt.js.org)がドキュメントが充実してて良さそうです。 明日やってみることにします。(altって名前がいけてないですね。AltjsってAltertative JSのことをさしたり、altjsっていうライブラリもありますし。。)
React Native with CSS Framwork
これも1時間ぐらい試行錯誤してできるのか試してみました。ググっても何もでてこないので、初stackoverflowに聞いてみました。明日に持ち越し。
http://stackoverflow.com/questions/29999964/react-native-with-css-framework