今更ながらReact Nativeに入門してみた①

# はじめに

実は最近、**React Native**なるものに入門してしまいました...。

React Nativeとはなんなのか？といったところの説明から、実際に簡易的なTODOアプリを作成するところまでご紹介できればと思います。

では、早速行ってみましょう〜！

# React Nativeとは何か

React Nativeとは、

**Reactを使って、iOSやAndroidなどのネイティブアプリを開発することができるクロスプラットフォームな開発ができるフレームワーク**です。

公式サイトは[こちら](https://reactnative.dev/)

# なぜReact Nativeで作るか

個人的にメリットだと感じる点はいくつかあります。

1. React(JavaScript)で書ける
2. プラットフォームを意識せず開発できる

普段JavaScriptでバックエンドやフロントエンドを書いている自分にとって、同じような感覚でネイティブアプリも作れてしまうのは非常に魅力的です！

また、本来であればiOSならSwift、AndroidならKotlinなど、プラットフォームごとに言語を使い分けるか、どちらかのプラットフォームに絞って開発していたものが、多少UIを変更するだけで良いのはユーザー獲得という観点でもメリットが大きいように思います。

また、ネイティブアプリだけでなく、WEBアプリ版も作成する時、WEBでもReactを使えば、アプリケーション自体はそのまま使い回し、UIのみ変更すれば良いので、そういう意味でもクロスプラットフォームな開発ができます。

# Expoとは何か

React Nativeによるネイティブアプリの開発は、Reactを使えることによってかなり開発しやすくなったとはいえ、普段WEBを開発しているエンジニアにとっては少しハードルが高くなります。

そんなネックを解消してくれるのが、**Expo**です。

Expoは、React Nativeによる開発をより簡単に、わかりやすくしてくれる開発用ツールです。

どう簡単にしてくれるかというと、

- ほぼReactでWEBアプリを開発している感覚でコードが書ける
- iOSアプリの場合、アプリのアップデートが再審査なしで行える

などがあります。

ただ、デメリットとして、

- 実装できる機能に制限がある

ことは念頭に置いておいた方が良さそうです。

公式サイトは[こちら](https://expo.io/learn)

# 簡易的なTODOアプリの作成

それでは、早速React Nativeを使ってTODO管理ができるアプリを作ります。

今回はアプリの配信はせず、ローカル環境で動作させるだけに留めます。

また、今回は、

- 開発のしやすさ
- 実装したい機能を全て実現可能

ということで、Expoも使っていきます。

まずは公式ドキュメントに沿ってReact NativeとExpoの環境構築を済ませておいてください。

プロジェクトを作成したいディレクトリで、Expoプロジェクトを作成します。

```shell
expo init todo_app_expo
```

プロジェクトを作成したら、プロジェクトのルートディレクトリでExpoを起動します。

```shell
expo start
```

## コンポーネントに切り分ける

まずは、作りたいものをなるべく最小単位で、コンポーネント化していきます。

今回はシンプルなTODOアプリなので、必要なコンポーネントは下記の通りです。

1. ユーザーの入力を受け付けるフォーム&ボタン
3. 登録されたTODOを表示する場所

もっと細かくコンポーネント化できるかもしれません。

その辺りは追々身につけていきます笑

## UIを作成する

次に、コンポーネントごとにまずは画面上に表示させてみましょう。

ExpoでiOSシミュレーターを起動させたら、自動でコードの変更を検知して更新されるような設定になっています。

コンポーネントは、ルートディレクトリ直下にcomponentsディレクトリを作成して、その下に配置していきます。

各コンポーネントの実際のコードは下記の通りです。

**./components/Form.tsx**

フォームの作成は、React Nativeの**TextInput**を使用します。

また、ボタンはReact Nativeの**Button**を使用します。

ドキュメントは[こちら](https://reactnative.dev/docs/textinput)

コンポーネントのソースは下記の通りです。

```jsx:./components/Form.tsx
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出すだけの仮実装です。

ソースは下記の通りです。

```jsx:./components/TodoList.tsx
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は完成とします。

現状はこんな感じです。

<img src="https://i.gyazo.com/49d0df752c4eb981829f0e24bc69e486.png" height="500">

## stateでコンポーネントの状態を保持する

さて、UIが完成したら、実際にTODOアプリとして動作するようにしていきます。

TODOアプリにするためには、

1. 入力したTODOを登録できる
2. 入力したTODOが一覧表示される
3. 完了したTODOを削除できる

上記の要件を満たす必要があります。

また、上記の要件を満たすためには、TODOを一時的なデータとしてクライアント側で保持しておく必要があります。

そういったことを実現するために、React Nativeには**state**という物が用意されています。

stateの公式ドキュメントは[こちら](https://ja.reactjs.org/docs/hooks-state.html)

stateとは、各コンポーネントの状態を保持するための機能で、

コンポーネントの状態とは、ここでいうTODOが登録されたり削除されたり、といったことを指します。

では早速、stateを用いてTODOの登録、一覧表示、削除を実装していきましょう。

### stateの定義・初期化

まずは、現状のTodoデータは下記のコードで定義しています。

```javascript
const todoList = ['TODO1', 'TODO2', 'TODO3'];
```

これをstateを使用するように記述すると、下記のようになります。

```javascript
const [list, setList] = useState(['TODO1', 'TODO2', 'TODO3']);
```

TODOリストなので、現状は空の配列でstateの値を初期化しておきましょう。

```javascript
const [list, setList] = useState([]);
```

これでコンポーネント内で値を保持しておくことができるようになりました！

### stateの値を表示・更新する

次は実際にstateで保持している値を表示して、更新するところまでです。

stateの値を使用するときは、下記のように使用できます。

```jsx
const [list, setList] = useState([]);

const FuncComponent = () => {
    return(
        <Text>{list}</Text>
    )
}
```

そして、値の更新は下記のように行います。

```jsx
const [list, setList] = useState([]);

const FuncComponent = () => {
    setList(['TODO']);
    return(
        <Text>{list}</Text>
    )
}
```

上記のコードだと、更新された値が表示されています。

少し解説すると、stateを作成するのに使用している「useState」ですが、引数には初期値を渡します。

今回のTODOアプリで言うと、空の配列を渡していますよね。

そして、useStateは実際のstateの値と、stateの値を更新する関数を返します。

上記のコードでは、それを変数に格納しているというわけです。

以上を踏まえて、実際に実装したTODO登録の関数とTODO削除の関数を作成しました。

(TODO登録の関数)
```javascript
const addList = (item: string) => {
    const newList = [...list, item];
    setList(newList);
}
```

(TODO削除の関数)
```javascript
const deleteList = (item: string) => {
    const newList = list.filter(li => { return li !== item });
    setList(newList);
}
```

一点注意点としては、stateの値の操作で配列を扱う際、

**破壊的なメソッドを使用するとリアクティブにレンダリングされなくなります**

ですので、値の追加にはpushなどは使わず、今回のようなスプレッド演算子を使うか、concatなどで追加します。

値の削除も同じように、popやshiftではなく、filterなどを使用すると良いでしょう。

## propsで親コンポーネントから子コンポーネントに値を渡す

最後に、切り分けたコンポーネント間で値を受け渡せるようにしましょう。

コンポーネントの階層としては、Appコンポーネントが親コンポーネントとなり、それぞれFormコンポーネントとTodoListコンポーネントを子コンポーネントとしています。

親コンポーネントから子コンポーネントに値を渡す際に使えるのが、propsです。

例えば、親コンポーネントで保持している数字を、子コンポーネントで表示する際は下記のようなコードになります。

(親コンポーネント)

```jsx
const ParentsFunc = () => {
    const [number, setNumber] = useState(0);
    
    return (
        <ChildFunc num={number} />
    )
}
```

（子コンポーネント）

```jsx
const ChildFunc = (props) => {
    return (
        <Test>{props.num}</Test>
    )
}
```

上記のように記述することで、親コンポーネントから渡ってきた値を子コンポーネントで使用することができます。

また、

```jsx
const ChildFunc = ({num}) => {
    return (
        <Test>{num}</Test>
    )
}
```

propsは受け取る時点で展開しておけば、記述量が少なくなります。

このpropsを利用して、同じ容量で先ほど作成した登録用の関数と削除用の関数を親コンポーネント内で定義し、子コンポーネント内で発火させるように実装していきます。

そしてついに完成しました！

<img src="https://i.imgur.com/H5LKsCq.gif" height="500">

完成品のコードは[こちら](https://github.com/aiirononeko/todo_app_expo)

# 終わりに

今回初めてReactNativeを使ってみましたが、ドキュメントも充実していて学びやすかったです。

WEBアプリと比べて、ネイティブアプリの方が一般の方に使ってもらいやすいサービスが作れそうでワクワクします！

次はReduxをある程度使えるようになったら、また記事にしてみようかなあ。