Table of Contents
はじめに
FundastA Inc.の片田です!
実は最近、React Nativeなるものに入門してしまいました…。
React Nativeとはなんなのか?といったところの説明から、実際に簡易的なTODOアプリを作成するところまでご紹介できればと思います。
では、早速行ってみましょう〜!
React Nativeとは何か
React Nativeとは、
Reactを使って、iOSやAndroidなどのネイティブアプリを開発することができるクロスプラットフォームな開発ができるフレームワークです。
公式サイトはこちら
なぜReact Nativeで作るか
個人的にメリットだと感じる点はいくつかあります。
- React(JavaScript)で書ける
 - プラットフォームを意識せず開発できる
 
普段JavaScriptでバックエンドやフロントエンドを書いている自分にとって、同じような感覚でネイティブアプリも作れてしまうのは非常に魅力的です!
また、本来であればiOSならSwift、AndroidならKotlinなど、プラットフォームごとに言語を使い分けるか、どちらかのプラットフォームに絞って開発していたものが、多少UIを変更するだけで良いのはユーザー獲得という観点でもメリットが大きいように思います。
また、ネイティブアプリだけでなく、WEBアプリ版も作成する時、WEBでもReactを使えば、アプリケーション自体はそのまま使い回し、UIのみ変更すれば良いので、そういう意味でもクロスプラットフォームな開発ができます。
Expoとは何か
React Nativeによるネイティブアプリの開発は、Reactを使えることによってかなり開発しやすくなったとはいえ、普段WEBを開発しているエンジニアにとっては少しハードルが高くなります。
そんなネックを解消してくれるのが、Expoです。
Expoは、React Nativeによる開発をより簡単に、わかりやすくしてくれる開発用ツールです。
どう簡単にしてくれるかというと、
- ほぼReactでWEBアプリを開発している感覚でコードが書ける
 - iOSアプリの場合、アプリのアップデートが再審査なしで行える
 
などがあります。
ただ、デメリットとして、
- 実装できる機能に制限がある
 
ことは念頭に置いておいた方が良さそうです。
公式サイトはこちら
簡易的なTODOアプリの作成
それでは、早速React Nativeを使ってTODO管理ができるアプリを作ります。
今回はアプリの配信はせず、ローカル環境で動作させるだけに留めます。
また、今回は、
- 開発のしやすさ
 - 実装したい機能を全て実現可能
 
ということで、Expoも使っていきます。
まずは公式ドキュメントに沿ってReact NativeとExpoの環境構築を済ませておいてください。
プロジェクトを作成したいディレクトリで、Expoプロジェクトを作成します。
| 
					 1  | 
						expo init todo_app_expo  | 
					
プロジェクトを作成したら、プロジェクトのルートディレクトリでExpoを起動します。
| 
					 1  | 
						expo start  | 
					
コンポーネントに切り分ける
まずは、作りたいものをなるべく最小単位で、コンポーネント化していきます。
今回はシンプルなTODOアプリなので、必要なコンポーネントは下記の通りです。
- ユーザーの入力を受け付けるフォーム&ボタン
 - 登録されたTODOを表示する場所
 
もっと細かくコンポーネント化できるかもしれません。
その辺りは追々身につけていきます笑
UIを作成する
次に、コンポーネントごとにまずは画面上に表示させてみましょう。
ExpoでiOSシミュレーターを起動させたら、自動でコードの変更を検知して更新されるような設定になっています。
コンポーネントは、ルートディレクトリ直下にcomponentsディレクトリを作成して、その下に配置していきます。
各コンポーネントの実際のコードは下記の通りです。
./components/Form.tsx
フォームの作成は、React NativeのTextInputを使用します。
また、ボタンはReact NativeのButtonを使用します。
ドキュメントはこちら
コンポーネントのソースは下記の通りです。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36  | 
						import React from 'react'; import { View, Button, TextInput, StyleSheet } from 'react-native'; const Form = () => {   const [value, onChangeText] = React.useState('');   const placeholder = 'テキストを入力してください'   return (     <View style={styles.form}>       <TextInput         style={styles.form}         onChangeText={text => onChangeText(text)}         value={value}         placeholder={placeholder}       />       <Button         onPress={() => {alert('pressed!')}}         title="Submit"         color="blue"       />     </View>   ) } const styles = StyleSheet.create({   form: {     height: 40,     width: '100%',     borderColor: 'gray',     borderWidth: 1,     margin: 'auto',   } }) export default Form;  | 
					
ボタンを押した時のalertは仮実装なので、後ほどきちんと作ります。
./components/TodoList.tsx
TODO一覧のコンポーネントは、現状ダミーのデータを用意しています。
ロジックとしては、TODOが配列で格納されているので、そのデータをReact NativeのFlatListでループ処理して表示しています。
削除ボタンは現状alert出すだけの仮実装です。
ソースは下記の通りです。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33  | 
						import React from 'react'; import { View, Text, FlatList, StyleSheet } from 'react-native'; const TodoList = () => {   const todoList = ['TODO1', 'TODO2', 'TODO3'];   return (     <View style={styles.list}>       <FlatList         data={todoList}         renderItem={({item}) => (           <View>             <Text style={styles.item}>{item}<Text onPress={() => {alert('delete!')}}> ×</Text></Text>           </View>         )}         keyExtractor={(item, index) => index.toString()}       />     </View>   ) } const styles = StyleSheet.create({   list: {     alignItems: 'center',     marginBottom: 50   },   item: {     fontSize: 22   } }) export default TodoList;  | 
					
あとは軽く配置を整えてとりあえずUIは完成とします。
現状はこんな感じです。

stateでコンポーネントの状態を保持する
さて、UIが完成したら、実際にTODOアプリとして動作するようにしていきます。
TODOアプリにするためには、
- 入力したTODOを登録できる
 - 入力したTODOが一覧表示される
 - 完了したTODOを削除できる
 
上記の要件を満たす必要があります。
また、上記の要件を満たすためには、TODOを一時的なデータとしてクライアント側で保持しておく必要があります。
そういったことを実現するために、React Nativeにはstateという物が用意されています。
stateの公式ドキュメントはこちら
stateとは、各コンポーネントの状態を保持するための機能で、
コンポーネントの状態とは、ここでいうTODOが登録されたり削除されたり、といったことを指します。
では早速、stateを用いてTODOの登録、一覧表示、削除を実装していきましょう。
stateの定義・初期化
まずは、現状のTodoデータは下記のコードで定義しています。
| 
					 1  | 
						const todoList = ['TODO1', 'TODO2', 'TODO3'];  | 
					
これをstateを使用するように記述すると、下記のようになります。
| 
					 1  | 
						const [list, setList] = useState(['TODO1', 'TODO2', 'TODO3']);  | 
					
| 
					 1  | 
						const [list, setList] = useState([]);  | 
					
stateの値を表示・更新する
次は実際にstateで保持している値を表示して、更新するところまでです。
stateの値を使用するときは、下記のように使用できます。
| 
					 1 2 3 4 5 6 7  | 
						const [list, setList] = useState([]); const FuncComponent = () => {     return(         <Text>{list}</Text>     ) }  | 
					
そして、値の更新は下記のように行います。
| 
					 1 2 3 4 5 6 7 8  | 
						const [list, setList] = useState([]); const FuncComponent = () => {     setList(['TODO']);     return(         <Text>{list}</Text>     ) }  | 
					
少し解説すると、stateを作成するのに使用している「useState」ですが、引数には初期値を渡します。
今回のTODOアプリで言うと、空の配列を渡していますよね。
そして、useStateは実際のstateの値と、stateの値を更新する関数を返します。
上記のコードでは、それを変数に格納しているというわけです。
以上を踏まえて、実際に実装したTODO登録の関数とTODO削除の関数を作成しました。
(TODO登録の関数)
| 
					 1 2 3 4  | 
						const addList = (item: string) => {     const newList = [...list, item];     setList(newList); }  | 
					
(TODO削除の関数)
| 
					 1 2 3 4  | 
						const deleteList = (item: string) => {     const newList = list.filter(li => { return li !== item });     setList(newList); }  | 
					
破壊的なメソッドを使用するとリアクティブにレンダリングされなくなります
ですので、値の追加にはpushなどは使わず、今回のようなスプレッド演算子を使うか、concatなどで追加します。
値の削除も同じように、popやshiftではなく、filterなどを使用すると良いでしょう。
propsで親コンポーネントから子コンポーネントに値を渡す
最後に、切り分けたコンポーネント間で値を受け渡せるようにしましょう。
コンポーネントの階層としては、Appコンポーネントが親コンポーネントとなり、それぞれFormコンポーネントとTodoListコンポーネントを子コンポーネントとしています。
親コンポーネントから子コンポーネントに値を渡す際に使えるのが、propsです。
例えば、親コンポーネントで保持している数字を、子コンポーネントで表示する際は下記のようなコードになります。
(親コンポーネント)
| 
					 1 2 3 4 5 6 7  | 
						const ParentsFunc = () => {     const [number, setNumber] = useState(0);     return (         <ChildFunc num={number} />     ) }  | 
					
(子コンポーネント)
| 
					 1 2 3 4 5  | 
						const ChildFunc = (props) => {     return (         <Test>{props.num}</Test>     ) }  | 
					
また、
| 
					 1 2 3 4 5  | 
						const ChildFunc = ({num}) => {     return (         <Test>{num}</Test>     ) }  | 
					
propsは受け取る時点で展開しておけば、記述量が少なくなります。
このpropsを利用して、同じ容量で先ほど作成した登録用の関数と削除用の関数を親コンポーネント内で定義し、子コンポーネント内で発火させるように実装していきます。
そしてついに完成しました!

完成品のコードはこちら
終わりに
今回初めてReactNativeを使ってみましたが、ドキュメントも充実していて学びやすかったです。
WEBアプリと比べて、ネイティブアプリの方が一般の方に使ってもらいやすいサービスが作れそうでワクワクします!
次はReduxをある程度使えるようになったら、また記事にしてみようかなあ。
                