こんにちは。
スマホアプリを作成するフレームワークであるReact Nativeをいちから勉強しようと思います。
せっかくなので、やってみたことの要点をまとめてあとから復習できるように記事にまとめました。
React Nativeをざっと知りたい方、基礎をざっと復習したい方は参考にしてみてください。
本記事の位置づけ
基本的に、公式ドキュメントの解説を上から順番に見て、ざっとまとめた記事です。
また、本ページは、下記公式ドキュメントの一番最初「The Basics」の部分をまとめた記事となります。
- Part1. The Basics ←今ココ★
- Part2. Environment setup
- Part3. Workflow
- Part4. Design
- Part5. Interaction
- Part6. Inclusion
- Part7. Performance
- Part8. JavaScript Runtime
- Part9. Connectivity
- Part10. Native Components and Modules
- Part11. Guides (Android)
- Part12. Guides (iOS)
なお、本ページに記載のソースコードはすべて上記サイトから引用したものとなります。
前提バージョン
React Native 0.63
React Nativeとは何か
Facebookが作成したオープンソースのモバイルアプリケーションフレームワークのこと。
iOSおよびAndroidのプラットフォーム向けに共通のコードでアプリを作成できる。
言語、フレームワークのベースはJavaScript、React。
用語
- View … UIを構成するブロックであり、画像を表示するImageView、テキストを表示するTextViewなどがある。
- Native Component … AndroidやiOSのプラットフォームのコンポーネントのこと
- Core Component … React Nativeが扱うコンポーネントのことで、各プラットフォームのネイティブコンポーネントと一対一対応している。
https://reactnative.dev/docs/intro-react-native-components#core-components
まずはHello World
コード
import React from 'react';
import { Text } from 'react-native';
const Cat = () => {
return (
<Text>Hello, I am your cat!</Text>
);
}
export default Cat;
上記はfunctionとしての定義だが、classとしても書ける(functionとclassはどちらで書いてもよい)。
ただfunctionのほうが機能追加の点で将来性があるとのことで、公式ドキュメントはfunctionをメインで書かれている。
プログラムの構造
- importでReactコンポーネントやReact NativeのText コアコンポーネントをインポートしている。
- 「const Cat = () => {};」の部分がfunctionの定義。これによりCatコンポーネントが定義できる。
- 「export default Cat;」でCatファンクションをプログラム内で呼び出せるようにする。
- <Text>~</Text>の部分は、JavaScript内にエレメントを記載するJSX記法。JSX記法自体はJavaScriptのため、変数を中括弧とともに内部に記載することが可能。
- JSX記法内で {getFullName("Rum", "Tum", "Tugger")} のように関数を呼ぶこともできる。
Props
propertiesの略で、関数に値を渡す機構。いわゆる、関数の引数。
...
const Cat = (props) => {
return (
<View>
<Text>Hello, I am {props.name}!</Text>
</View>
);
}
const Cafe = () => {
return (
<View>
<Cat name="Maru" />
<Cat name="Jellylorum" />
<Cat name="Spot" />
</View>
);
}
...
関数で「Cat = (props)」のように記載することで、propを受け取ることができるようになる。
値の渡し方は、JSX記法内に「name="Maru"」のように記載する。
関数内では、「{props.name}」の記載で値を参照できる。
propsではJSオブジェクトを受け渡せるので、下記のような記載も可能。
<Image
source={{uri: "https://reactnative.dev/docs/assets/p_cat1.png"}}
style={{width: 200, height: 200}}
/>
curly bracesが二重になっている理由:内側の{}はJSオブジェクトの記載方法。外側の{}はJSXで受け渡すための記載。
State
関数の状態を保持する。いわゆる内部変数。
持てる値の型は、strings, numbers, Booleans, arrays, objects。
stateを使う3ステップ
1.useStateをインポートする。
import React, { useState } from 'react';」
2.useStateを使い、getter、setter、初期値をセットする。
[<getter>, <setter>] = useState(<initialValue>).
例
const [isHungry, setIsHungry] = useState(true);
3.getter、setterで値を読み書きする。
setterの例
<Button
onPress={() => {
setIsHungry(false);
}}
/>
getterの例
<Button
disabled={!isHungry}
title={isHungry ? 'Pour me some milk, please!' : 'Thank you!'}
/>
あれ?isHungryはconstで定義したのになぜ値が書き換えられるの?
⇒setterが実行されると大元の関数が再度実行され、関数自体がリセットされている。なので値を書き換えているわけではない。
上記全体を含めた例
import React, { useState } from "react";
import { Button, Text, View } from "react-native";
const Cat = (props) => {
const [isHungry, setIsHungry] = useState(true);
return (
<View>
<Text>
I am {props.name}, and I am {isHungry ? "hungry" : "full"}!
</Text>
<Button
onPress={() => {
setIsHungry(false);
}}
disabled={!isHungry}
title={isHungry ? "Pour me some milk, please!" : "Thank you!"}
/>
</View>
);
}
const Cafe = () => {
return (
<>
<Cat name="Munkustrap" />
<Cat name="Spot" />
</>
);
}
export default Cafe;
<>~>って何?
⇒JSXの断片(fragments)。エレメントを複数並べて書く場合は必ず別のエレメントでくくる必要があるが、Viewなどを使うとネストしてしまうので、単にエレメントをまとめたい場合はこのように書くことができる。
PropsとStateの使い分け
Propsは関数に値を渡すことで動作を変えたいとき、Stateは関数に状態(値)を保持したいときに使う。
Core Component:Text Input
文字列を入力できるコアコンポーネント。
よく使うprop
- onChangeText … テキストが変更されたときに発火する。
- onSubmitEditing … テキストがsubmitされたときに発火する。
例
import React, { useState } from 'react';
import { Text, TextInput, View } from 'react-native';
const PizzaTranslator = () => {
const [text, setText] = useState('');
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={text => setText(text)}
defaultValue={text}
/>
<Text style={{padding: 10, fontSize: 42}}>
{text.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
</View>
);
}
export default PizzaTranslator;
このプログラムは、入力された単語の数だけピザアイコンを画面表示するもの。
onChangeTextで、setterであるsetTextを呼び出し、入力された値をTextにセットしている。
TextInputの詳細は下記を見るべし。
https://reactnative.dev/docs/textinput
Core Component:ScrollView
スクロールができるコンテナ。
複数の異なる種類のコンポーネントやビューを含むことができる。
horizontalプロパティで、垂直スクロール、水平スクロールかを指定する。
...
export default App = () => (
<ScrollView>
<Text style={{ fontSize: 96 }}>Scroll me plz</Text>
<Image source={logo} />
<Image source={logo} />
</ScrollView>
);
pagingEnabledプロパティで、スワイプジェスチャーを可能にする。
maximumZoomScale、minimumZoomScaleプロパティで、拡大縮小ができるようにする。
ScrollViewとFlatListどっちを使う?
データ数が少ない場合は、スクロールの際リスト全体を再描画するScrollViewのほうが軽量。
データ数が多い場合は、スクロールの際リスト全体ではなく画面に表示されている部分のみを再描画するFlatListが高速。
Core Component:List Views
複数の項目をリストで表示するコンポーネント。
FlatList、SectionListの2種類ある。
FlatList
FlatListは下記2つのプロパティを指定する必要がある。
- data … 表示するリストデータ
- renderItem … リストの各項目に適用する処理
FlatListの例
import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22
},
item: {
fontSize: 18,
height: 44,
},
});
const FlatListBasics = () => {
return (
<View style={styles.container}>
<FlatList
data={[
{key: 'Devin'},
{key: 'Dan'},
{key: 'Dominic'},
{key: 'Jackson'},
{key: 'James'},
{key: 'Joel'},
{key: 'John'},
{key: 'Jillian'},
{key: 'Jimmy'},
{key: 'Julie'},
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
);
}
export default FlatListBasics;
FlatList
https://reactnative.dev/docs/flatlist
SectionList
もしセクションごとに分かれたデータを表示したい場合(セクションにヘッダを付けたいなどのケース)は、 SectionListを使うとよい。
<SectionList
sections={[
{title: 'D', data: ['Devin', 'Dan', 'Dominic']},
{title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},
]}
renderItem={({item}) => <Text style={styles.item}>{item}</Text>}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
keyExtractor={(item, index) => index}
/>
dataの代わりにsectionsプロパティで、セクション付きの値を指定。
renderItemに加えて、renderSectionHeaderプロパティでセクションヘッダのスタイルも指定。
keyExtractorで、リストデータのキーとして使う値を指定。
SectionList
https://reactnative.dev/docs/sectionlist
Troubleshooting
React Nativeのセットアップ、実行時にエラーが出たら、下記を参照のこと。
https://reactnative.dev/docs/troubleshooting
https://github.com/facebook/react-native/issues/
プラットフォーム依存のコード
AndroidやiOSのネイティブコードを書きたいとき、下記2つの方法がある。
- Platformモジュールを使用する方法
- ファイルの拡張子を使用する方法
各モジュールには特定のプラットフォームでしか使用されないプラットフォーム依存のプロパティがあり、それらは @platform と指定されている。
使用方法は各プロパティ項目にリンクがはられているのでそれを参照するとよい。
Platformモジュールを使用する方法
ネイティブコードをごく少量使う場合は、Platform.OSを使った書き方で。
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100
});
Platform.OSの値は、「ios」または「android」になる。
ある程度のコード量になる場合は、Platform.selectを使った書き方で。
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
...Platform.select({
ios: {
backgroundColor: 'red'
},
android: {
backgroundColor: 'green'
},
default: {
// other platforms, web for example
backgroundColor: 'blue'
}
})
}
});
Platform.selectの値は、'ios' | 'android' | 'native' | 'default'。
Webでの表示の場合は、defaultに入る。
iOS/AndroidとWebで処理を書き分けたければ、以下のようにnativeで判定するとよい。
const Component = Platform.select({
native: () => require('ComponentForNative'),
default: () => require('ComponentForWeb')
})();
<Component />;
iOS/Androidの場合は、nativeの処理が実行される。
Webの場合は、defaultの処理が実行される。
プラットフォームのバージョンを取得する
Android
import { Platform } from 'react-native';
if (Platform.Version === 25) {
console.log('Running on Nougat!');
}
Platform.Versionにて取得。
iOS
import { Platform } from 'react-native';
const majorVersionIOS = parseInt(Platform.Version, 10);
if (majorVersionIOS <= 9) {
console.log('Work around a change in behavior');
}
Platform.Versionには、-[UIDevice systemVersion]の実行結果が入っており、"10.3"のような値となっている。
そのため、parseIntなどを使って数値を抽出する。
ファイルの拡張子を使用する方法
プラットフォーム依存コードをファイルごとに書き分け、ファイル名で判定する方法。
.ios.または.android.という文字列が含まれるファイルを作成することで、実行するプラットフォームに応じて適切なほうが読み込まれる。
より多くのプラットフォーム依存コードがある場合はこちらの方法で。
BigButton.ios.js
BigButton.android.js
のようにファイル名で分けておき、
import BigButton from './BigButton';
とすることで、プラットフォームに合わせて適切なほうのファイルが読み込まれる。
ネイティブ環境とWeb環境で書き分けたいときは、「.native.」も使える。
Container.js
Container.native.js
のようにファイル名で分けておき、
import Container from './Container';
とする。
おわりに
ここまでが「The Basics」の章の内容となります。
引き続き順番に公式ドキュメントを読んで勉強していきます。
少しでもご参考になったならば幸いです。