更新日時: 2018/7/25
- Reactは知っている
- ReactNative(以下RN)を触ったことがなく、ReactNative for Web(以下RNW)で実装したい
RN、RNWを「初めて」実装するにあたり、
必要な知識と、解決方法を記している
CreateReactApp(以下CRA)から作るかCreateReactNativeApp(以下CRNA)から作るのか私感と、
実装していて思ったことをつらつらと書いている
- 0.前提
- 1.フォルダ構成
- 2.RNW
- 3.RNWを実装するにあたりCRNAとCRA、どちらで作ったほうがいいか
- 4.実装を始める前に
- 5.WebとNativeでの分岐方法
- 6.開発方法の違い
- 7.使えるコンポーネント
- 8.Android開発
- 9.デバッグ
- 10.困ったこと解決したこと
- 11.その他
- 参照記事
- 謝辞
- author
node 8.11.1 npm 5.6
yarn install
json-server db.json
yarn ios or yarn web
expoがインストールされていること
実機確認
- AppStore でExpo Client or expoと検索。ダウンロード
0-1-1 Expo
JavaScriptでnativeをビルドできるライブラリとサービスのセットツール
ExpoSDKは参照を提供する。コンポーネントのようなイメージ。
カメラやソーシャルログインなどの機能的なシステムへ参照。
SDKはexpo(npm package)によって提供され、
npm install --save expoでインストールされる
import {Contacts} from 'expo'
or
import Expo form 'expo'`
ExpoSDKモジュールをインポートできる
ローカルデベロップツール。プロジェクトをこちらから作ったり、ビルドやパブリッシュするために使う。
XDEでもプロジェクトを作るとreact、react-native、expoをインストールする。
XDEの代替ができるコマンドラインcli。
いくつかのExpoの機能はこれを使うことを必須としている。
開発したアプリをデバイス上でopenするツール。
シュミレータとエミュレータにはインストールしないでいい。
XDEかexpoでプロジェクトをサーブするとExpoClientでプレビューできるURLを生成する。
AndroidではExpoClientはexpo.io上の他のprojectも描画できたりする
ExppoClientはデバイス上、シュミレータ、エミュレータ上で動く。
CRNAはプロジェクトに設定されたExpoClientによってサポートされたRNの最新バージョンをサポートする
ExpoClientはRNがバージョンアップされた1週間後ぐらいにそのバージョンをサポートする
Andoriodエミュレータ。
仮想デバイスを作り出す。ドキュメントではNuxus5、バージョンは任意がオススメとされている。
オラクルvirtualBoxを利用していて、もしまだインストールしていなければ使う際にインストールする必要がある
- 以下の1-2を元にカメラロールで写真選択、サムネ表示、POST処理を実装したプロジェクト
- CRNAから作られた基盤(react-router,redux,redux-saga等)だけの雛形
- CRAから作られた基盤(react-router,redux,redux-saga等)だけの雛形
Web対応する際は設定が必要
-
package.json内
-
jest-expo
-
react-native-scripts(Webからnativeのコードを実行するためのmodule)
-
mainの記述変更(mainはnativeのentryポイントを渡す)
-
react-nativeを動かすためのpackage(互換性のあるバージョンで指定している。バージョンをあげる際は慎重に) ・expo@^25.0.0(app.jsonに記載) [email protected] react-native-web バージョンを合わせる必要がある
-
.watchmanconfigを追加 コードからシュミレータへの即時反映を提供している
-
app.jsonを追加(app.jsonは、コードに属していないアプリケーションの部分を設定するためのもの。)
-
src内
-
プロジェクト直下にApp.test.jsを追加(RNのエントリーポイントテスト。名前は変えないこと)
//package.json内でmainに渡しているファイル内
//react-native-scripts/src/bin/crna-entry.js
import Expo from 'expo';
import App from '../../../../App';
import React, { Component } from 'react';
import { View } from 'react-native';
if (process.env.NODE_ENV === 'development') {
Expo.KeepAwake.activate();
}
Expo.registerRootComponent(App);//Expoに対してmainコンポーネントを設定している
Expoに対してAppをroot登録している(native側のエントリーポイントの名前を変えない理由)
RNのコンポーネントとAPIをReactDOMを使ってWeb上で利用できるようにしたもの
- 書いたコンポーネントはプラットフォームを選ばない
- ベンダープレフィクスのサポート
- touch,mouse,keyboradなどのインプットモードをサポート
- ReactDevToolsによる開発
- RTL(Right-to-Left)アラビア系言語のサポート
サポートしているブラウザ
Chrome, Firefox, Edge, Safari 7+, IE 10+.
hoge.web.js内でRNが提供しているものを使う場合
RNWが提供しているコンポーネントしか使えない。
つまりRN <- RNW <- 共通のところでjsで使えるもしくはweb.jsで使える。
多くはView、Textになると思う。
ViewやTextはブラウザ描画でdevやspanに置き換わる
共通のコンポーネント内でもPlatformで分岐すればいいので、あまりこだわらないでいい
それよりはexpoのコンポーネントがどこまで実装したい機能として使えるのか、
ネイティブの機能がどこまで使えるのかを知ることの方が
大事な印象。
なぜならそれによってネイティブエンジニアのアサインが必要になるかもしれないし
Webは別機能で、別ライブラリで近いことを実装しましょうで解決できる気がするから。(今回のカメラロールとドロップファイルのように)
開発するにあたっての学習コストの使い方は
Native開発の知識 > RNの知識 > RNWの知識
という順番な印象
特定のタグにしたい場合
<View accessibilityRole="article" /> => <article role="article" />
<Text accessibilityRole="link" /> => <a role="link" />
と書く必要がある
共通化しているところ(containerなど)では下記のことが注意する必要がある。
- div、pなどは使えない
- Text > Link はエラー。 Link > Text
- ul > li は FlatListのdata、renderItem(propsにmap)で表現する
Link
//work web, but don't work Native
<Link>Hoge</Link>
//ok
<Link><Text>Hoge</Text></Link>
text
//error 「text cannot be used outside of a <Text>...
<View>
{count}
</View>
//ok
<View>
<Text>
{count}
</Text>
</View>
- RNでhtmlタグを記述する場合createElementを使ってtagを作ることができる
感想
- 共通化しなければRNWはそんなに気にしなくていい。 web.js内で分けて記述すればいい。
- こっちではエラー、こっちでは表示されているみたいなことが多い→RNのコンポーネントがwebに対応しているか、
Webの記述をnative記述に置き換える修正ul -> FlatList など
styleはRNで定義されている
例えば
<Button
style={styles.buttonColor}
/>
const styles = StyleSheet.create({
buttonColor: {
backgroundColor: "red",
color: "#fff"
}
});
これは効かない Buttonに渡せるpropsを確認する
Color of the text (iOS), or background color of the button (Android)
なので
colorはiosだとtextに、androidだとbackgroundにスタイルされる
<Button
color="#2196f3"
/>
とする必要があるし 何かRNのコンポーネントでスタイルしたい場合、 Viewでラップしてスタイルする 例えば、
<View style={style.some}>
<Button
color="#2196f3"
/>
</View>
RNWはCRAとCRNAでデフォルトで対応されている
CRNAのhybrid-app-native
ではwebpack.config.js
のL1120
にある'react-native': 'react-native-web'
は
nativeの処理をwebに渡している(react-nativeのコンポーネントをimportすればract-native-webのそれをimportしたことになる)
CRAでは同じことをnode_modules/react-scripts/のconfig。webpack.config
内でやっている
実装ではあまり設定を気にすることはない
CRNA。 CRAより設定の手間がない。
どのような機能を持ったWebAppを作りたいかが重要
ネイティブコードをいじる可能性があるならexpoに頼らずxcode、AndroidStudioで作ることになるので
用意が変わってくる
RNWは前述のようにRNのコンポーネントを使えたら使えるようにするだけなので
どちらかというとRNでの開発経験やネイティブの知識が重要になってくることがわかる
分岐の方法は2種類で
・ファイル拡張子を変える
・コード上で分岐する がある
Moduleを共通化したい場合
react-native-web/#compatibility-with-react-native
を参照して、どのModuleが共通化できるか確認する必要がある
CameraRoll
の場合RNWとRNで互換性はないので共通化できないので分岐する必要があることがわかる
分岐の方法は以下。
hybrid-app-native/src/common/
の
Routing.native.js
Routing.web.js
のように
拡張子をnativeとwebで分けることによって
プラットフォームがそれぞれ実行時に適切なファイルを見る そのwebpackが解決している箇所は下記
webpack.config.js extensions: ['.web.js', '.js']
注意点は、
exportする際のオブジェクト名を同じにしてimport時に同じ名前を呼び出すこと
例えば共通のものを親Componentで呼ぶ際には
import { Timer, Clock } from '../common/Timer'
とimportして
Timer.web.js
Timer.native.js
内で同じ名前Timer
をexport
すること
今回react-router-native
とreact-router-dom
がそれをしている
Platformを使う
import { Platform, View, Text, StyleSheet, Button } from "react-native";
<Text>{Platform.OS === "web" && "web is here"}</Text>
<Text>{Platform.OS === "ios" && "ios is here"}</Text>
RNのカメラはimage-pickerのライブラリがあるが、 それはexpo配下では使えないため(nativeコードをいじる必要がある) expoが提供しているAPIを使うことになる
ざっくり3つの道がある。
CRNAにはexpoがデフォルトで入っている。 expoはRNで使われている開発やビルドを助けるツール。
expoに乗っ取って作るかexpoをejectするかでその先の開発でできることが変わる
- expoを通してクラウド上でビルドできる
- すぐにデプロイできる
- ホットリロードで開発できる。
- ExpoプロジェクトはiosやAndroidフォルダがなく 100%JavaScript。これは自分自身でビルドすることはできないことを 意味している
- Expoのオープンソースコンポーネントが使える
- ExpoAppはhostアプリケーションの中で実行され、 app storeから独立して更新をpushできる 新しいビルドを行うことなくコードを公開することを意味する
- AppleDeveloperAccount($99/year)なしにテストできる
react-native initで作られるprojectのことと同じ方法になる
- iOSとAndroidのプロジェクトをフォルダに含む
- XcodeやAndroidStudioでビルドできる
- JavaScriptでかけるがカスタムする際はnativeCodeをいじる
- 利用可能な多くのコンポーネントを使用できる
eject後も、Expokitというライブラリで必要なcomponentを使うことができ、
全てnativeComponentに置き換えることはしないでもいいし完全にnativeに行くこともできる
Sharing Code between React Web and Native Apps
npm install -g react-native-cli
react-native init [AppName]
後、トップレベルにweb階層を作る。
create-react-native iniit
をした後expoをejectして作った方が楽そう
expoはどのように実行しているのか
RNはJSで実行しているコンパイル済みのアプリ。
RNプロジェクトをbuildし、実行すると、PakagerがMetroという名前で起動する。
packagerは下のことをする
- 1.全てのJSコードを一つのファイルに結合し、デバイスが解析できないJSXや新しいJS構文を変換する
- 2.Imageコンポーネントによって描画できるようにassetsをオブジェクトに変換する
Expoを使わない場合
react-native start
Expoを通すと
exp start
これらはpakagerを立ち上げている 違いは exp startはExpo Development Serverというものを呼び出す。 このサーバーはRNのpackager(Metro)によって作られたJavaScriptバンドルを取得し、 Expo appのsimulator上で実行するプロセスを実行します。
expoで作るAppをpublishする
ユーザーはexpoをダウンロードして 、その中で作られたアプリを実行する 提供されて作ったアプリのurlを介して
exp publish
実装している最中にこの機能が使いたいと思い、
それはRNWでできるのかexpoのコンポーネントなのか、RNから引っ張ってくるのか選択するケースがある
その場合注意しなくてはいけないのが、
react-native-linkのこと。
ReactNativeライブラリで使いたいライブラリがnativeコードに依存しているものもあるので
それを使いたい場合linkコマンドを打つ必要がある。
その場合expoをejectしなければならない。
具体的にいうと RNで使える全てのライブラリは
にあるが、
いくつかのライブラリは純粋なJavaScriptだがいくつかのライブラリはネイティブコードに依存している。
そのライブラリを使う場合、
react-native link [packagename]
を打ち、
アプリケーションにインストールする必要がある
(linkコマンドはnativeプロジェクトの中にインストールされたnode packageをinstallするコマンド。それはNativeProjectでのみ起こすことができる)
例としてある人は、
AppleMapかGoogleMapをiOS上で使えるreact-native-mapsを使いたかったが
linkコマンドを実行しなくてはいけなかった。
この場合ejectする必要がある
機能を追加する際はその仕様がExpoだけでいけるのかnativeもいじることになるのかを
先に調べて実装する必要がある
AndroidSDKとgenymotionを使って開発する or 実機にexpoをインストールして開発する
AndroidStudio から AndroidStudioをダウンロード
AndroidStudio -> Configure -> SDKmanager
でAndroid Locationのパスを確認 多くの場合下記
/Users/{username}/Library/Android/sdk
使っているshellにそのパスを通す 自分の場合.zshrcに以下を追加
export PATH="$PATH:/Users/moritakenji/Library/Android/sdk/platform-tools"
更新
source ~/.zshrc
adb(AndroidDebugBridge)をターミナル上で確認
adb
次に genymotion(Androidエミュレータ)をdownload、連携させる
こちらはからダウンロード genymotion
genymotionにAndroidStudioToolsの先ほどのパスを認識させる
genymotion > setting > ADB
adb-serverのバージョンとSDKのバージョンが合っていないとエラーになるので注意
googlePlayからexpoをダウンロード yarn androidでexpoのQRコードスキャンで確認する。
npm install -g react-devtools
run
react-devtools
もしWaiting for React to connectが出てきたら
ここをコピペして
index.htmlにぺ
sumilator上でcmd + d, reload bundle
出てきます
simulatoreが立ち上がっている状態で
Cmd + D
Debug Remote JS
開発ツールを立ち上げて 該当のソースを参照する
cmd + d でReload JS Bundle
その画面上部のRefreshを押下
どちらのプラットフォームにも気を使いながら開発する WebとRN同時に開発していると コードを変更した際に当然Webとnativeプラットフォームも更新されます。
例えばWebでinputタグを使っていても
RN側では静かにエラーになっています
Invariant Violation: View config not found for name input
この場合、View configの中にinputという名前のものはないよといわれています。 また
This error is located at:
in input (at Photo.js: 10)
とあるのでそこで互換性のない記述がされているかもしれません
今回の場合 inputがtype属性がtextならTextInputをreact-nativeから呼んで使うことで解決します
デバッガーを立ち上げた際にでる
Remote debugger is in a background tag which may cause apps to perform slowly.
Fix this by foregrounding the tab
いくつかのlocalhostを開いているタブを閉じてください`
Reload JS
を押下
or
yarn ios
(serverをたちあげる)
node -v
v6.10.2
だと出るエラーnvm use --lts
で最新のnodeを使ってください
Unable to resolve ../../../../App" from ".//node_modules/react-native-scripts/build/bin/crna-entry.js`: The module `../../../../App` could not be found"
立ち上がっているsimulator上でcmd + d
, Reload JS Bundle
してみる
simuratorを立ち上げる前にyarn iosするとでる(これは期待しない)
terminalでもエラーになっている
Failed to start simulator:
Error: Process exited with non-zero code: 60
Exiting...
もう一度yarn ios
すればとりあえず治る
PCのIPアドレスと実機のIPアドレスの相違を疑う
https://qiita.com/noraworld/items/264d944f3ac8074e454d
yield call(post, formData)//call内のpostが適切か確認
AndroidSDKとadbのバージョンが合っていない
10-10 Error running adb: No Android device found. Please connect a device and follow the instructions here to enable USB debugging:
もしGenymotionを使っている場合、 SDKへのパスをGenymotionのABDへ渡すか、実機で検証する
debug -> Slow Animationsのチェックを外す
抽象的なレイヤーである「Bridge」を使ってnative widgetsとしてレンダリングする
Bridgeとは・・・ホストプラットホーム上のレンダリングAPIによって実行される
RNはメインスレッドをブロックすることなく、
プラットホームのJSエンジンを使用して、
別スレッドでアプリケーションを実行するレイヤー
[参照](https://www.logicroom.co/react-native-architecture-explained/)expoで認証機能を作るとなるときついことがあるらしい。FireBaseなら簡単らしい
これを公開するにあたり株式会社 m-Labさんに御礼申し上げます