Takashi Okusawa
Dec 20, 2017 · 20 min read

最初に

私がReact Nativeでの開発をはじめた当初は、日本語での情報がまだ多くありませんでした。React Nativeのセットアップやライブラリの紹介が多く、開発を進めていくと参考になる情報は少なくなっていきました。
私が知りたかったのは、実際にアプリを最後まで作った方の経験談でした。ちょっと触っただけではなく、実際にアプリをリリースされた方の生きた声を聴きたかった。

React Nativeを使った理由

React Nativeに対する印象は、その人ができる or 好きなプログラムであったり、開発環境の好みであったりによって大きく変わるのでは?と感じています。そこで私がReact Nativeで感じるメリットを書く前に、私がどんな開発を行なってきたかを少し書きます。

過去の開発経験

PIAZZA株式会社でサーバーサイド全般、androidアプリ、分析全般を行なっています。 webサービスに関わるようになったのはここ4年ほどで、その前はc/c++でゲームや組み込み寄りの仕事がメインでした。

Reactの経験

2015年9月から2016年8月まで、開発に参加したプロジェクトでReact/Fluxを使っていました。

ReactNativeに至るまで

2015年9月

PIAZZAのandroidアプリは、AndroidStudioを使ってjavaでプロトタイプを作っていた時期がありました。(2015年9月〜10月頃)

  • レイアウト調整がだるい。
  • API覚えるのもだるい。
  • ストアにリリースした事は無いが、昔やった事がある開発の延長なので新鮮味が無い。

2016年2月

React Native for Androidをはじめて試したのは、2016年2月頃。0.21のリリース前だったのを覚えています。私には以下が大きなメリットでした。

  • ホットリロードが素晴らしい。UIの調整がラク。
  • React経験があったので、新しく覚えるべき事柄がほとんどない。

2016年6月

必要な機能についてのライブラリを再度調査した結果、3ヶ月前に比べると選択肢が大分増えていました。これならやれるとReact Nativeで行くことを決断。

  • immutable.js
  • flow/typescript
  • 非同期処理
  • カメラロール(ギャラリー)へのアクセス
  • ピンチズームによる画像の拡縮
  • ViewPager
  • スプラッシュ画面
  • メッセージ画面

情報源

開発スタート時点(2016年7月)

参考にしたページ

2016年8月以降

ある程度コードを書き始めると、必要な情報は英語でしか得られなくなってきました。

  • エラーやトラブルでググると大体、ReactNativeや関連ライブラリのissueへたどり着く事が多くなる。
  • facebookグループ: React Native Community
  • twitter, medium

開発について

ここからはPIAZZAのandroidアプリをどう実装していったかについて書きます。
開発時のメモをがあるので、そこから当時苦労した事も載せます。
今は必要ない情報ですが、今後何かあった時に過去のトラブルが頭に入ってると、解決のヒントになると思うので。

開発態勢

開発環境

emaceの設定

こちらを参考にしました。

主要ライブラリ

flow

独自に型定義は作らず、単純に変数と引数の型チェックのみ。

redux

action, reducerを書くのがだんだん面倒になってきて手を入れたくなってきたが、独自仕様を追加すると今後人が増えたときに混乱するかなーと思って愚直に書き続けている。

Immutable.js

reducerでしか使っていない。

redux-saga

こちらの記事で知って、公式のドキュンメント読んだらすぐしっくり来たのでそのまま使ってます。

react-native-router-flux

v3.40.1を使用中。 2016年7月時点では、これがrouterの第一候補でした。クセはあるが、 動かすまでが早かった。今後はv4にするか他のライブラリへ移行するか考え中…

React Native FBSDK

2016年8月の導入時は、コンパイルエラーが発生して自力で解決した。
facebookログイン、投稿のシェア、招待機能に使用。

superagent

Reactでの開発時に使っていたので、そのまま使用。
画像のpostが動かなかったので、こちらを参考に投稿やプロフィール編集のみ、XMLHttpRequestを使っている。

デバッグ

  • reactotronを使ってます。
  • デバッグprintはconfファイルを作ってconsole.logとReactotron.logを切り替えられるように。
  • version名に開発verと分かる特定の文字列を入れて、開発verの場合はapi接続先を選択可能に。

ディレクトリ構成

プロジェクトルートにsrcを作ってソースコードはそちらに。

── src
│ ├── Routes.js // scene定義
│ ├── actions // action定義
│ ├── apis // apiコール
│ ├── components // component置き場
│ ├── config // 設定関連
│ ├── constants // 固定値置き場
│ ├── containers // reduxと接続する画面を管理するcomponent。
│ ├── decls // 未使用
│ ├── reducers // reducer置き場
│ ├── sagas // redux-saga
│ ├── store // createStore
│ └── utility // 雑多

ソースファイル

component/containerの典型的なコード構成は以下のような感じです。

/* -*- mode: web -*- */
import React from 'react'
// importいろいろ
// @flowconst styles = StyleSheet.create({
// style定義いろいろ
});
type Props = {
// props定義
};
type State = {
// state定義
}
export default class HogeView extends React.Component {
constructor(props) {
super(props);
// 初期化処理
}
state State; // react lifecycle method
componentWillReceiveProps(nextProps) {
// setStateしたり
}
// callbacks
onPress() {
}
// その他関数 render() {
return (
// 表示
)
}
}

開発初期の実装の流れ

実装系統を以下のようにわけて、それぞれの系統から1つずつ先に実装。
その後、同じ手順で残りの画面を実装していった。

  • ログイン周り
  • 投稿/プロフィール編集(post系画面)
  • その他

実装のポイント

画面への反映

遷移状況によって、同じ画面が違うパラメータで呼ばれしまうケース。
例えば、投稿詳細A ▶ あるユーザーのタイムライン ▶ 投稿詳細B
このとき、投稿詳細Bで通信が走ると投稿詳細用のreducerでstateが更新されるため、投稿詳細Aでも、componentWillReceiveProps()が呼ばれる。
何も考えずにnextPropsを受け取って更新してしまうと、投稿詳細Aと投稿詳細Bが同じ内容になってしまうので、投稿詳細で表示する内容は画面内のstateに持ち、nextPropsを受け取った際は、内部stateと(例えば)idが一致した場合のみ更新するようにした。

いいねを複数画面へ反映

タブが複数あり、それぞれのタブでタイムラインや投稿が表示される。
タイムラインにも投稿詳細画面にもいいねボタンがあり、どこでいいねをしても、自分のアクションが反映されている必要がある。

deep link

  1. 通常のandroidアプリと同様に、AndroidManifest.xmlにintent-filterを追加
  2. routeとなるスクリーンの componentDidMount() で Linking.addEventListener(‘url’, this.handleDeepLink)
  3. 同様にcomponentWillUnmount() で Linking.removeEventListener(‘url’, this.handleDeepLink)
  4. handleDeepLinkの実装は以下のような感じ
handleDeepLink(event: Object) {                                                                       
// event.urlにpathが入ってるので、それに合わせて画面遷移
}

文字列のtruncate

最初はsubstr使って文字数で制御してたが、こちらのブログでText compoentに標準で用意されてるの知りました。行数で指定できるので便利。公式ドキュメントをちゃんと読まねばダメだなと思った次第。

通信と画面遷移のanimationが重なると処理落ち

componentWillMount()でfetchを呼ぶ画面が、通信状況によってどうしても引っかかるような挙動になる。
対策として以下を試しているが、解決に至っていない。

  • componentWillMount()でfetchを呼ぶ。取得した値はcacheして、画面遷移animation終了後にsetState()して画面更新かける。

push通知

  • react-native-fcmを使用
  • サーバー側はAWS sns。api keyはFirebaseのダッシュボード▶project settings▶CLOUD MESSAGINGにあるServer key (legacy token)というのを使う。
  • FCMへ送るjsonはこちらを参考に

クリッピボードにコピー

React Nativeに用意されている。
https://facebook.github.io/react-native/docs/clipboard.html

animated header

こちらを参考に実装。

汎用ScrollViewの作成

タイムライン内で横スクロールリストを表示する必要があるため、ListViewは使わずScrollViewを使っていた。
開発が進むうちにリスト系の表示に使う処理をまとめるために汎用ScrollViewを作成して全てのリスト表示はこれを使うようにした。主な機能は、

  • 引っ張って更新
  • スクロールしたら自動追加fetch

タブレットでのレイアウト崩れ

画像を幅一杯に表示する場合に高さを数値で指定すると意図した比率にならない。(当たり前か。。) 基準の画面サイズから比率を割り出し、スクリーンサイズに応じて高さを計算するようにした。

modal

こちらのコードを参考に自作した。

その他使用ライブラリ

React Native AutoLink

文字列への自動リンクに使用。
自動でリンクが押せるようになるが、当たり判定がやや上にずれてる?

react-native-lazyload

遅延描画component。

react-native-datepicker

イベント投稿の開始/終了日時に使用。
当初はプロフィール編集の生年月日入力にも使っていたが、android純正のdatepickerは年の選択が分からりづらいとの声があり、そちらは自前実装に。

react-native-image-zoom

androidのみ対応の画像拡縮Comnponent。
modalでラップして使用してます。

react-native-gifted-chat

メッセージ画面に使用。
素晴らしいライブラリです。1日で画面実装できた。

react-native-action-sheet

投稿のmore menu等で使用。

react-native-collapsible

ライセンス画面で使用。

react-native-swiper

walkthroughで使用。

react-native-animatabl

♡をタップした際のanimationに使用。

react-native-device-info

versionの取得に使用。

react-native-vector-icons

検索用のicon等に使用

react-native-icon-checkbox

チェックボックス

react-native-image-picker

ギャラリー

react-native-modalbox

上から下に降りてくるmodalが欲しかったので使用。

react-native-scrollable-tab-view

ViewPager

react-native-shared-preferences

SharedPreferences

rn-splash-screen

スプラッシュ画面

過去のハマり

React Native

0.33.0

onActivityResultのinterface変更
プロジェクト内の全てのライブラリで新しい書式に対応していないと落ちる。最初fbログインで落ちてたのでReact Native FBSDKをずっと調べてたが、結局違うライブラリを最新版にupdateしたら解決した。

0.34.0

TextのlineHeightが1行目のみ正しく表示されない(0.34.1で解決)

0.39.0

css-layoutのバグ修正のため、子compoenetからflex: 1を削除しないとレイアウトが崩れるように

0.42.0

TextInputで改行が入力できない
※現在は修正された模様

0.44.0

updagra時にトラブル。 https://github.com/facebook/react-native/issues/13390 https://github.com/facebook/react-native/issues/13314

"react-addons-pure-render-mixin": "^15.5.0",
"react-static-container": "^1.0.1",
"react-native-router-flux": "3.39.1"

react-native-autocomplete-input

入力補完用component。(現在はPIAZZAの仕様変更により未使用)
ScrollView内でこのComponent使う場合は、keyboardShouldPersistTapsをScrollViewにセットしないといけない。

スプラッシュ画像がresizeされてクラッシュ

1920*1080しか画像を準備していなかったため高解像度の端末で拡大される際にメモリ不足でクラッシュした。
drawable-xxxhdpiに大きいサイズのスプラッシュ画像を置いて解決。

react-native-gifted-chat

huaweiの一部端末でメッセージが表示されない

根本の問題はこれ。RN0.48.0では修正されてるとのこと。

  • native-invertible-scroll-viewにpatchをあてる

React Native FBSDK

ビルドエラー(リンク先を参照してください)

emailが取れなくなった

これはFacebook api側の挙動が変わったせいだろうか? 最初に実装した時は問題無かったが、ある時から

LoginManager.logInWithReadPermissions(['public_profile', 'email'])

リリース

コマンドラインから、

cd android && ./gradlew assembleRelease

終わりに

以上のような感じで、React Nativeによるandroidアプリを開発/運営しています。 振り返ってみると、多少なりともjavaでのandroid開発経験があること、そしてReactも経験済みだった事が導入を決心する上で大きな要因になったのかなと思います。どうしてもダメだったらjavaで何とかなるだろうと。

Takashi Okusawa

Written by

programmer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade