はじめに
こんにちは、株式会社OKANで「おかんPay」というプロダクトのPdMをしながらも、自らバリバリReact Nativeで実装している かなすぎ と申します。
元々は、RailsでOKANの社内システムを構築していたサーバーサイドエンジニアだったのですが、志願してReact Nativeを使うアプリエンジニアになってから約半年が立ちました。
そこで、RailsエンジニアがReact Nativeを扱うアプリエンジニアになる時に苦しんだ箇所をまとめることで、自分と同じようにReact Nativeを新たに勉強する人の助けになればと思いながら記事を書きました。
想定読者
- Railsエンジニアだが、これからReactNativeアプリもつくってみたいなって思っている人
- React.js, Vue.jsなどフロントエンドフレームワークをがっつり実装したことないけど、React Nativeでアプリ作ってみたい人
RailsエンジニアがReactNativeエンジニアになるにあたって苦しんだところワースト3
RailsではJavascriptも使うことはあったのですが、MVCでいうViewのDOM操作をjQueryで軽く行うくらいの経験値であったので、ES6でがっつりロジックを書くというのも覚えることが多くて大変でした。また、Vue.jsやReact.jsなどで用いられる状態管理や、React.js特有のライフサイクルメソッドやJSX。そして、ウェブ開発では存在しないアプリ特有のDeepLinkや画面遷移の方法なども大きく異なっているので、キャッチアップに苦労しました。
ワースト1位:ES6(ES2015)
React NativeはES6で書きます。Javascriptは、ES6以降とそれより前では使える機能は大きく変わりました。現在流行りのフロントエンドのフレームワークであるAngular.js、React.js、Vue.jsなどでも、基本的にはES6以降の書き方で書くことが多いです。JavascriptはViewの見た目を動的に変える軽量な言語という認識でしたが、ES6を勉強してからは、認識が大きく変わりました。
イマドキのJavaScriptの書き方2018 - Qiita
ES2015(ES6) 入門 - Qiita
アロー関数
ES6以前では、関数を定義する時は、functionを使用していましたが、ES6以降では、アロー関数を用います。記号ばっかりで最初は慣れないかと思いますが、慣れれば、短い記述で関数を定義できるので非常に便利です。()を省略できたり、様々な記述パターンがあるので、引数の数など場合によって使いわけましょう。
// 何もしない関数
() => {}
// 引数が一つだったら「()」不要
value => {}
// ブロックないが1行しかないときは、「{}」不要
(a, b) => a + b
// オブジェクトは「()」が必要
(a, b) => ({ a: b, b: a })
// 複数行はいつも通り
(a, b) => {
const c = a * 100;
return {
c,
d: b / 100,
};
}
アロー関数
JavaScript アロー関数を説明するよ - Qiita
アロー関数を使って効率的にJavaScriptを記述する
import / export
モジュール毎に機能を管理する時に使います。モジュールで機能を管理することによって、他コードとの依存性を少なくして保守性を高めたり、変数名の競合を防いだり、汎用性の高いモジュールをつくって同じコードを繰り返すのを防いだりできます。
import React from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
Image,
TouchableHighlight,
Alert,
Linking,
} from 'react-native';
export default ItemList;
export const calories;
export function pay(){...}
export class Payment {...}
import
export
ES6のexportについて詳しく調べた - Qiita
非同期処理 async / await
// 非同期のpayメソッド
const pay = async (amount) => {
const items = await getItems();
const totalAmount = amount + tax;
try {
await this._payByCreditcard(totalAmount, items);
return true;
catch(e) {
console.warn(e);
return false;
}
};
非同期処理には、かなり苦しめられました。Rubyは同期処理できる言語なので、コードを書いたら上から順に実行されます。しかし、Javascriptは非同期であり、たとえばAPIをコールしても、その結果を待たずに、次の行のコードの処理を進めることができてしまうのです。そこで、Promiseオブジェクトを用いて非同期処理を行い、asyncで非同期関数を定義し、awaitで非同期の結果が帰ってくるまで待つことができるのです。
JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる - Qiita
【JavaScript入門】誰でも分かるPromiseの使い方とサンプル例まとめ! | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
async/await 入門(JavaScript) - Qiita
ワースト2位:React
React NativeではReactを使ってAndroidアプリとiOSアプリを作ることができます。したがって、React自体の状態管理であったり、JSXなどReact独特の考え方・概念をキャッチアップする必要があります。フロントでReactを使用したことがあれば問題ないのですが、RailsをAPIモードで使用していた経験もなく、SlimでViewを書いていたので、Reactの概念を理解するのも大変でした。
React Native · A framework for building native apps using React
React - ユーザインターフェース構築のための JavaScript ライブラリ
Component
ReactはComponent志向で作られます。Railsエンジニアだと、Componentとは?というところのキャッチアップから始める必要があります。Componetをざっくり説明すると、UIを独立させて再利用できるようにして、propsという任意の値を親要素から受け取り画面上に表示させるものです。この文章だけ読んでもわけが分からないと思うので、サンプルコードなどを見ながら理解すると、理解が深まると思います。
// ■ Reactのコンポーネントたち
// 【Component】
// Stateを持っていて、再レンダリングの条件を自分でチューニングしたいときに使う。
class FooComponent extends React.Component {
render() {
return <View />;
}
}
// 【PureComponent】
// Stateを持っていて、再レンダリングの条件をチューニングしないときに使う。
class BarComponent extends React.PureComponent {
render() {
return <View />;
}
}
// 【Functional Component】
// Stateを持たなくて良い場合に使う。
const FuncComponent = props => {
return <View />;
};
フロントエンドのコンポーネント設計に立ち向かう - Qiita
状態管理(state)
stateとは、Componentの状態を管理するデータです。たとえば、isPayableというフラグがたっていたら支払いのロジックを動かすということをDBでフラグを管理しなくても、フロント側で管理することができるのです。Reactでは、eduxという状態管理のライブラリを使用することが多いのですが、弊社のアプリは軽量なアプリなのでunstatedというライブラリを用いて状態管理をしています。個人的には、Reduxは冗長になってしまうので、unstatedの方が直感的でわかりやすくオススメです。
[React Native] 基本を学ぶ - Stateでコンポーネントの状態を管理する | Developers.IO
コンポーネントの state - React
jamiebuilds/unstated
次の状態管理はReduxをやめてunstatedにする理由 - Qiita
props
Componentは、propsという任意の引数(正確には引数ではありません)を受け取って、値を当てはめたReact要素を返します。初めてReactを勉強した時は、stateとpropsをごっちゃにしてしまい、それぞれの役割を正確に理解できませんでしたで苦労ポイントです。
[React Native] 基本を学ぶ - Propsでコンポーネントのパラメータを設定する | Developers.IO
コンポーネントと props - React
JSX
JSXはReactのComponent内でのXML風の構文です。React.jsでは、HTMLでお馴染みの<h1>
タグなどが利用できるのですが、React Nativeでは、<View>
や<Text>
といった見慣れないタグを扱います。さらに、アプリエンジニアの方は理解しやすいそうなのですが、とかとかアプリ独特のComponentを使用する必要があるので、実際に動かしてみれば、「あーこれか。アプリで使ったことある」と思うのですが、Componentの名前などを覚えるのには一苦労しました。
ActivityIndicator · React Native
class Payment extends React.Component {
// ここがstateで状態をComponent内で管理している
state = {
isPayable: true,
items: {},
totalAmount: 0,
};
_onPressPayment = () => {
{ isPayable } = this.state;
if (isPayable) {
const result = await pay(amount);
if (result) {
this.setState({ isPaylable: false});
}
}
}
render() {
return (
// <Text>タグや<TouchableHighLight>がJSX
// onPress={this._onPressPayment}がprops
<TouchableHighlight onPress={this._onPressPayment}>
<Text>支払いをする</Text>
</TouchableHighlight>
)
};
}
ライフサイクルメソッド
ここが個人的には一番ReactNativeエンジニアになって辛かったです。未だに、あれこれってComponentDidMountであってるよね?って確認したくなります。Reactでは複数回renderメソッドが走るので、どのタイミングではどんなデータを用意してなければならないといったことも考慮しなくてはならないので、奥が深いです。さらにReactHooksでの書き換えも「おかんPay」では行っているので、useEffectなどの使い方は悪戦苦闘しています。
// 色々なライフサイクルメソッドがあります。
componentDidMount()
componentDidUpdate(prevProps, prevState, snapshot)
componentWillUnmount()
【React Native】React Native ライフサイクルメソッドについて|Fresopiya
React component ライフサイクル図 - Qiita
ワースト3位:ReactNativeアプリやアプリ開発特有の概念
Web開発では存在しない概念であったり、Web開発の場合と概念が異なることがあり、Railsエンジニアの方にとっては、新しく勉強することが多いです。そこで、自分がReactNativeエンジニアになるにあたって特に苦労したことをまとめました。
React Navigationの画面遷移
画面遷移については、Railsでもある概念です。Routes.rbでルーティングを設定して、redirectやrenderを用いて画面遷移を実現します。しかし、ReactNativeなどアプリの開発では、画面を重ねていく、つまりStackしていくといったイメージの方がしっくりくると思います。RailsでWeb開発だけをしていた自分にとっては、なかなか理解するのに時間がかかるところでした。ライブラリによっても仕様は異なるのですが、弊社ではReact Navigationを使用しています。
React Navigation · Routing and navigation for your React Native apps
React Navigation 3.x チートシートつくってみた - Qiita
DeepLinkとかLinking
アプリ同士の連携をするのには、DeepLinkやLinkingというモジュールをReact Native開発では使います。アプリエンジニアにとってはお馴染みの概念なようなのですが、Railsエンジニアの自分にとっては、未知の世界でした。urlを叩いたりするとアプリを開いたりする機能はこうやって実現するのかと素直に感心した領域です。日本語のドキュメントが少ないので、調べるのも英語だったので一苦労でした。今回紹介するのは、React NavigationのDeepLinkになります。
Deep linking · React Navigation
Linking · React Native
ディープリンクをめぐる歴史とReact NativeにFirebase Dynamic Linksを導入する手順 - KitchHike Tech Blog
アプリのリリース
iOSとAndroid共にリリースを何度も経験をしているのですが、慣れれば作業なのですが、一番初めのリリースはこんなにも時間がかかるものなのかと驚愕しました。また、弊社プロダクトの「おかんPay」のReactNative版を初リリースしようとしたタイミングでAndroidの審査も始まったらしいというタイミングだったので、てんやわんやでした。さらに、Google Play ConsoleのUIや仕様はかなり頻繁に更新されてしまうので、Qiitaの記事なども最新記事を追わないないと、「そのボタンどこにあるの?」みたいなことは多発しました。初めてのアプリリリースには、余裕をもったスケジュールにすることをオススメします。
iPhoneアプリ申請やAppleの審査に関するメモ - Qiita
Androidアプリにも審査がされるようになった件 - Qiita
React NativeでiOS, Androidのストア公開のTips - Qiita
iOSとAndroidのレイアウト差分
iOSとAndroidでは、PickerやCalendarで日付を選択する時など、多くの場合で、レイアウトが異なります。ReactNativeたった1つの言語でiOSとAndroidの両方のアプリを作れることは非常に大きなメリットではあるのですが、両者を全く同じに扱って言い訳ではなく、レイアウトをPlatformによって変更する必要があります。
また、エミュレータと実機の差分もあったり、Androidでは、HuaweiやSonyといった機種依存でレイアウトが変わったり、Androidのバージョンによってもレイアウトが崩れたりします。したがって、Androidすべての機種で実機テストをすることはできないので、レンタルサービスなどを用いてテストしたりしています。
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
width: Platform.OS === 'ios' ? 200 : 100,
});
【React-Native】iOS, Android両対応する。 - Qiita
配備機種一覧 - 実機検証用スマホ・モバイルデバイスレンタル|テスト受託サービスのケータイラボ
最後に
いかがだったでしょうか?Railsエンジニアには馴染みのない概念が多かったと思います。
この記事だけでは、もちろん十分ではないものの、自分がここ半年で苦労した箇所を中心にまとめてあるので、何かの助けになれば幸いです。
次は、SocialDogのアプリ開発している@t0m0120さんです。
私もSocialDogは、Twitterを活用するのに利用させてもらっています。どんな記事が読めるのか楽しみです!