NCMBのReact Native SDKを使ってTodoアプリを作る（その2：Todoデータの登録と一覧表示）

NCMBでは公式SDKの他、コミュニティSDKとして各種言語、フレームワーク向けにSDKを提供しています。今回はReact Native SDKを使ったTodoアプリ開発を解説します。

[前回は各画面やコンポーネントの説明、そしてSDKの導入方法について解説しました](https://qiita.com/goofmint/items/4c299ba44750d8dd1085)。今回はTodoデータの登録と、一覧表示を解説します。

## コードについて

今回のコードは[NCMBMania/Expo-Todo-App: React Native SDKで作ったTodoアプリです](https://github.com/NCMBMania/Expo-Todo-App)にアップロードしてあります。また、Expo Snackでも使用できます。

[NCMB-Expo-Todo-App - Snack](https://snack.expo.dev/@goofmint/1706f4)

## Todoの新規入力について

![FireShot Capture 097 - タスクの編集 - localhost 20211011161343 - 097のコピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/fcba5cfb-5d39-122d-380f-8badaf76abc4.jpeg)


Todoの入力はInput.jsにて行います。まず、入力テキスト用のStateを用意します。

```js
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { TextInput } from 'react-native-paper';
import { NCMBObject } from './ncmb';

export default function InputTodo(props) {
  // 入力テキスト用
  const [text, setText] = React.useState('');
}
```

画面描画内容は次のようになります。

```js
return (
  <View style={styles.container}>
    <TextInput
      label="タスク"
      mode="flat"
      dense="true"
      style={styles.textInput}
      value={text}
      onChangeText={(text) => setText(text)}
      onKeyPress={keyPress}
    />
  </View>
);
```

そして、テキストボックスでエンターキーを押したタイミング（IME確定は除く）で、データを保存します。利用するのは NCMBObject になります。NCMBObjectはDBでいう行相当のデータになります。 `set` メソッドでデータを適用し（カラムに対する値の設定に相当します）、saveメソッドで保存します。

```js
// エンターキーを押した際にデータストアに保存する
const keyPress = async (e) => {
  if(e.which === 13){
    try {
      // タスクを新規保存します
      const obj = new NCMBObject('Todo');
      await obj
        .set('body', text)
        .set('checked', false)
        .save();
      props.added(obj);
      setText('');
    } catch (e) {
      console.log(e);
    }
  }
};
```

`props.added` はHome.jsから送られてきている、データ追加後に実行する関数です。

```js
// タスクを追加した後に呼ばれるアクション
const added = (obj) => {
  ary.push(obj);      // 配列にタスクを追加
  setArray([...ary]); // 配列を更新
  // バナーでメッセージ表示
  setMessage('タスクを追加しました');
};
```

## Todoの一覧表示について

Todoの表示はHome.jsにて行います。

![FireShot Capture 097 - タスクの編集 - localhost 20211011161343 - 097.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/6368aad0-364d-d194-e1fd-bd78709baf17.jpeg)


```js
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import { List, Button } from 'react-native-paper';
import InputTodo from './Input';
import { NCMBQuery } from './ncmb';
import TodoItem from './TodoItem';
import Banner from './Banner';

export default function Home({ navigation }) {
}
```

まずTodoの配列が入る変数（ary）、バナーでのメッセージ内容を管理する変数（message）を用意します。

```js
// Todoの配列が入る変数  
const [ary, setArray] = useState([]);
// バナーで表示するメッセージ
const [message, setMessage] = useState('');
```

次に初期表示したタイミングで、データを取得します。この時にはNCMBQueryを使います。引数はクラス名（DBでいうテーブル相当）です。

```js
// タスクを取得する関数
const fetchTodo = async () => {
  // NCMBのTodoクラス（DBでいうテーブル相当）を準備
  const query = new NCMBQuery("Todo");
  // 取得する件数
  query.limit = 100;
  // データを取得
  const ary = await query.fetchAll();
  // 配列にセット
  setArray(ary);
};

// 最初に一度だけ実行する処理
useEffect(() => {
  fetchTodo();
}, []);
```

後は描画内容として、このaryをFlatListで表示します。

```js
// 描画
return (
  <View style={styles.container}>
    <List.Section>
      <List.Subheader>
        <View style={{ flexDirection: 'row'}}>
          <Text>既存のタスク</Text>
          <View
            style={{flexDirection: 'row', justifyContent: 'flex-end' }}
          >
            <ShowDelete />
          </View>
        </View>
      </List.Subheader>
      <FlatList
        data={ary}
        renderItem={({index, item }) => <TodoItem item={item} index={index} navigation={navigation} updated={updated} />}
        keyExtractor={item => item.get('objectId')}
      />
    </List.Section>      
    <InputTodo added={added} />
    <Banner
      message={message}
    />
  </View>
);
```

`ShowDelete` は、削除対象のタスクが1つでもあれば、ボタンを有効にします。

![FireShot Capture 097 - タスクの編集 - localhost 20211011161344 - 097.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/dfb625c6-e85a-4ab5-7213-3b9ec1955517.jpeg)


```js
// 削除ボタンの有効、無効を切り替えつつ表示する  
const ShowDelete = () => {
  return (
    <Button
      icon="delete"
      mode="contained"
      onPress={deleteTask}
      // 1件でも完了したタスクがあれば、削除を表示
      disabled={!(ary.filter(item => item.get('checked')).length > 0)}
    >
      <Text>削除</Text>
    </Button>
  )
};
```

`TodoItem` は一行あたりのTodoを表示するコンポーネントです。

```js
// リスト（各行）の表示処理
return (
  <List.Item
    title={item.get('body')}
    // リストをタップしたら、編集画面に遷移する
    onPress={() => navigation.push('Detail', { index, item, updated })}
    // タスクの完了状態を見てチェックボックスを変更
    left={props => <Checkbox
      status={item.get('checked') ? 'checked' : 'unchecked'}
      // チェックボックスをタップしたら、完了状態を反転
      onPress={() => checked({index, item})}
      />
    }
    // > を表示
    right={props => <List.Icon
        icon="chevron-right"        
      />
    }
  />
)
```

## まとめ

ここまでの処理で、NCMBObjectを使ったデータ保存と、NCMBQueryを使ったデータ検索が完成します。次回はデータの更新処理について解説します。

## おまけ

NCMBのReact Native SDKを使ったハンズオンは随時開催しています。ぜひチェックしてください。

[【お昼のハンズオン】React Native × NCMBでTodoアプリを作ろう \- connpass](https://ncmb.connpass.com/) など