【ReactNative】TODOアプリを作ってみた

# はじめに
今回は、ReactNativeを用いてTODOアプリを作成していく。

環境構築がまだの方は、こちらを参考にお願いします。

https://qiita.com/muranakar/items/8d0cb2044fe836fe758e

今回はこちらのライブラリーを用いて実装していきます。

https://reactnativeelements.com/

https://github.com/oblador/react-native-vector-icons


# 内容
### ライブラリーをインストール
```
npm install react-native-elements react-native-vector-icons
```

### タスクの変数と関数を用意
```react
  const [task, setTask] = useState('');
  const [tasks, setTasks] = useState([]);
```
`task`と`tasks`の状態変数:
- `task` は現在のタスクの文字列を格納します。ユーザーが新しいタスクを入力する際に、この状態変数が更新されます。
- `tasks` はタスクのリストを格納します。追加された各タスクはこのリストに保存されます。


### タスクを追加する関数を定義

```react
const addTask = () => {
    if (task.trim() !== '') {
      setTasks([...tasks, task]);
      setTask('');
    }
  };
```
この関数は、新しいタスクをタスクリストに追加するために呼び出されます。
最初に、`task`の値が空でないかチェックします。空でない場合、新しいタスクを`tasks`配列に追加します。
追加後に、`task`の値を空文字して、新しいタスクを入力しやすくします。

### タスクを削除する関数を定義
```react
  const removeTask = (index) => {
    const newTasks = [...tasks];
    newTasks.splice(index, 1);
    setTasks(newTasks);
  };
```
この関数は、指定されたインデックスのタスクをタスクリストから削除するために呼び出されます。
`newTasks`という新しい配列を作成し、元の`tasks`配列から指定されたインデックスのタスクを削除します。


## コンポーネントの作成

### タスクのテキストフィールド

```react
 <TextInput
          placeholder="新しいタスクを追加"
          value={task}
          onChangeText={(text) => setTask(text)}
    />
```
`TextInput` コンポーネントは、新しいタスクを入力するためのテキストフィールドです。`value`プロパティに`task`の値を反映しており、`onChageText`内で新しいタスクの名前を変数に保存している。


### タスク追加のボタン

```react
<TouchableOpacity onPress={addTask}>
    <Icon name="add" size={30} color="white" />
</TouchableOpacity>
```
`TouchableOpacity`コンポーネントは、タスクを追加するためのボタンです。ユーザーがこのボタンをタップすると、　`addTask`関数が実行されます。
`TouchableOpacity`コンポーネントを用いることで、ボタンタップ時に透明度を下げることができます。

### タスクのリスト表示

```react
<FlatList
    data={tasks}
    keyExtractor={(item, index) => index.toString()}
    renderItem={({ item, index }) => (
        <View>
            <Text>{item}</Text>
            <TouchableOpacity onPress={() => removeTask(index)}>
              <Icon name="delete" size={20} color="red" />
            </TouchableOpacity>
        </View>
    )}
/>
```
タスクリストの表示は、`FlatList`コンポーネントを使用して表示されます。このコンポーネントはスクロール可能なリストを作成し、
- `data`プロパティに タスクリスト状態変数を反映
- `keyExtractor`プロパティに　一意のキー値を持たせるため`index`の文字列を設定
- `renderItem`プロパティに　List内の要素のコンポーネントを作成して設定

各タスクは個別の項目として表示され、`renderItem`プロパティに指定された関数によってレンダリングされます。

他にもプロパティが用意されているので参考までに御覧ください。

https://reactnative.dev/docs/flatlist


### タスク削除ボタン

上記のタスク項目内に実装されています。
```react
<TouchableOpacity onPress={() => removeTask(index)}>
    <Icon name="delete" size={20} color="red" />
</TouchableOpacity>
```
削除ボタンのコンポーネントを作成して、各項目の中に含まれています。
このコンポーネントをタップすることにより、タスク削除の処理が呼び出されます。


## 完成
iOS
![Simulator Screen Recording - iPhone 15 Pro - 2024-01-22 at 10.05.55.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799713/09b8ba7a-e416-8ae9-4dc3-3618ae49916a.gif)

Web

![2024:01:22TODOWeb.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799713/702084be-f358-5915-8845-f3894928670a.gif)


## まとめ
<details><summary>サンプルコード</summary>

```react
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, FlatList, StyleSheet } from 'react-native';
import { Icon } from 'react-native-elements';

const ToDoApp = () => {
  const [task, setTask] = useState('');
  const [tasks, setTasks] = useState([]);

  const addTask = () => {
    if (task.trim() !== '') {
      setTasks([...tasks, task]);
      setTask('');
    }
  };

  const removeTask = (index) => {
    const newTasks = [...tasks];
    newTasks.splice(index, 1);
    setTasks(newTasks);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.header}>ToDoアプリ</Text>
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          placeholder="新しいタスクを追加"
          value={task}
          onChangeText={(text) => setTask(text)}
        />
        <TouchableOpacity style={styles.addButton} onPress={addTask}>
          <Icon name="add" size={30} color="white" />
        </TouchableOpacity>
      </View>
      <FlatList
        data={tasks}
        keyExtractor={(item, index) => index.toString()}
        renderItem={({ item, index }) => (
          <View style={styles.taskItem}>
            <Text>{item}</Text>
            <TouchableOpacity onPress={() => removeTask(index)}>
              <Icon name="delete" size={20} color="red" />
            </TouchableOpacity>
          </View>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: 'white',
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    marginTop: 100,
    marginBottom: 20,
  },
  inputContainer: {
    flexDirection: 'row',
    marginBottom: 10,
  },
  input: {
    flex: 1,
    height: 40,
    borderWidth: 1,
    borderColor: 'gray',
    marginRight: 10,
    paddingHorizontal: 10,
  },
  addButton: {
    backgroundColor: 'blue',
    justifyContent: 'center',
    alignItems: 'center',
    width: 40,
    height: 40,
  },
  taskItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 10,
  },
});

export default ToDoApp;

```
</details>

今回は、ReactNativeでTODOアプリの実装を行いました。作成したTODOアプリはタスクがメモリ上に残るのみであり、一度アプリを落とすと消えてしまいます。そのため、次回以降にローカルDB　または、Firestore上にデータ保管する機能を実装してみたいと考えています。

## 最後に
他にも良い方法があれば、コメントいただけると大変うれしいです。
良かったと思ったら、いいねやTwitterのフォローよろしくお願いいたします！

https://sites.google.com/view/muranakar

個人でアプリを作成しているので、良かったら覗いてみてください！