Expo + React Native でSQLiteデータベースを用いる

サクッとスマホアプリが作れるExpoでのSQLiteデータベースを使いたい時の話

Expoで使われるDBはfirebaseやmongoDBが多いようですが、
「とにかく楽に作ろうと思っているのに新しいこと覚えてられるか！」という僕みたいな人もいるはずだと思い、記録します。

## 環境

OS: ubuntu 16.04 LTS

``` package.json
{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "eject": "expo eject",
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@expo/samples": "2.1.1",
    "babel-core": "^6.26.3",
    "babel-jest": "^24.1.0",
    "babel-preset-env": "^1.7.0",
    "expo": "^32.0.0",
    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
    "react-native-elements": "^1.1.0",
    "react-navigation": "^3.0.9"
  },
  "devDependencies": {
    "babel-preset-expo": "^5.0.0",
    "jest-expo": "^32.0.0"
  },
  "private": true
}
```

## 導入

ExpoにAPIが用意されているので、それをimportするだけ。

``` App.js
import {SQLite} from 'expo'
```

## データベース作成

``` App.js
const db = SQLite.openDatebase(name)
```

nameという名前のDBを取得します。
存在しなければ作ってくれます。

以降で用いるdatabaseオブジェクトをreturnするため、変数として定義する使い方になります。

## SQL実行

createやselectのための関数は用意されておらず、全てSQLの実行でデータベース操作を行います。
以下のようなコードでSQLを実行します。

### id, name の2つのカラムを持つ players テーブルを作成

``` App.js
  const db = SQLite.openDatabase('db')

  db.transaction(tx => {
    tx.executeSql(
      'create table if not exists players (id integer primary key not null, name text);', // 実行したいSQL文
      null, // SQL文の引数
      () => {console.log('success')}, // 成功時のコールバック関数
      () => {console.log('fail')} // 失敗時のコールバック関数
    );
  },
  () => {console.log('fail')}, // 失敗時のコールバック関数
  () => {console.log('success')} // 成功時のコールバック関数
)
```

select文などの実行によりデータベース内のデータを取得し表示する、といった場合は、
```executeSql``` の第3引数(成功時のコールバック関数)に実装します。

### データベースに登録された players を取得し、 state に格納

``` App.js
  db.transaction(tx => {
    tx.executeSql(
      'select * from players',
      null,
      (_, { rows: { _array } }) => {this.setState({players: _array})} // 第2パラメータに格納されているクエリの結果から取得した配列(_array)を、stateに格納する
    )
  },
  () => {console.log('fail')},
  () => {console.log('success')}
  )
```

## 実行例

Sato, Ito, Kato の3人の playerをテストデータとして insertし、
データベースから取得し表示しています。

``` HomeScreen.js
import React from 'react';
import {SQLite} from 'expo'
import {
  Text, View, FlatList
} from 'react-native';
import{
  ListItem, Icon
} from 'react-native-elements'

const DB = SQLite.openDatabase('db')

function insertPlayer(name){
  console.log('insert Player, name:' + name)

  DB.transaction(tx => {
    tx.executeSql(
      `insert into players (name) values (?);`,
      [name]
    );
  },
  () => {console.log('fail')},
  () => {console.log('success')},
  );
}

function insertTestData(){
  console.log('insertTestData')
  
  insertPlayer("Sato")
  insertPlayer("Ito")
  insertPlayer("Kato")
}

insertTestData()

export default class HomeScreen extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      players: null
    }
  }

  componentWillMount(){
    DB.transaction(tx =>{
      tx.executeSql(
        'create table if not exists players (id integer primary key not null, name text);'
      ),
      null,
      DB.transaction(tx => {
        tx.executeSql(
          'select * from players',
          null,
          (_, { rows: { _array } }) => this.setState({players: _array})
        )
      }
      ) 
    })
  }

  static navigationOptions = {
    title: 'players'
  };

  render() {
    const {players} = this.state

    return (
      <FlatList
        data={players}
        renderItem={({item}) =>{
          return(
            <ListItem
              key={item.id.toString()}
              title={item.name}
            />
          )
        }}
        keyExtractor={item => item.id.toString()}
      />
    );
  }
}
```

![Screenshot_20190303-221140.jpg](https://qiita-image-store.s3.amazonaws.com/0/203131/103f1c1e-9dfa-00e3-99cb-c211ba854bcc.jpeg)

## 参考文献

公式のAPIドキュメント
https://docs.expo.io/versions/latest/sdk/sqlite/

公式の使用例アプリケーション
https://github.com/expo/sqlite-example

## さいごに
なにか間違いなどがあればご指摘お願いします。