あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

React Native+ExpoでSQLiteのデータベースにアクセスする方法

はじめに

React Native + Expo + TypeScript な環境で SQLite のデータベースにアクセスする方法を試してみたのですが、なかなかうまくいかず苦労したので、その方法を掲載します。

そもそもとして、ローカルのデータベースには Realm (レルム)を使用したかったのですが、Expo は Realm をサポートしていないので泣く泣くサポートされている SQLite を使用することにしました。

準備

以下のコマンドをプロジェクトのルートディレクトリで実行して、Expo のライブラリをインストールします。

$ expo install expo-sqlite
$ expo install expo-file-system

実装サンプル

SQLite を実行するサンプルを以下に掲載します。

既存のソースから抜粋しているので、動作するとは思いますがその前提で参照してください。

import React, { useState } from 'react';
import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';

const SQLiteSample: React.FunctionComponent = () => {

  const [items, setItems] = useState<SQLite.SQLResultSet>();

  const initializeDatabase = () => {
    // DBの作成先を出力
    console.log(FileSystem.documentDirectory + 'SQLite/');

    // DBのオープン
    const db = SQLite.openDatabase('DB.db');

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

            return true; // ロールバックする場合はtrueを返す
          }, // 失敗時のコールバック関数
        );

        tx.executeSql(
          'insert into players (id, name) values (?, ?),(?, ?);',
          [1, 'yamada', 2, 'sato'],
          () => {
            console.log('success');
          }, // 成功時のコールバック関数
          () => {
            console.log('fail');

            return true; // ロールバックする場合はtrueを返す
          }, // 失敗時のコールバック関数
        );

        tx.executeSql(
          'select * from players where id = ?;',
          [2],
          (_, resultSet) => {
            // SUCCESS
            setItems(resultSet);
          },
          () => {
            console.log('fail');

            return false; // 何もしない
          }, // 失敗時のコールバック関数
        );
      },
      () => {
        console.log('fail all');
      }, // 失敗時のコールバック関数
      () => {
        console.log('success');
      }, // 成功時のコールバック関数
    );
  };

  const showData = () => {
    for (let i = 0; i < items.rows.length; i++) {
      const id = items.rows.item(i).id;
      const name = items.rows.item(i).name;
      console.log(`${id}:${name}`);
    }
  };

  const deleteData = () => {
    const db = SQLite.openDatabase('DB.db');

    db.transaction(
      tx => {
        tx.executeSql(
          'delete from players;',
          null,
          () => {
            console.log('success');
          }, // 成功時のコールバック関数
          () => {
            console.log('fail');

            return true; // ロールバックする場合はtrueを返す
          }, // 失敗時のコールバック関数
        );
      },
      () => {
        console.log('fail');
      }, // 失敗時のコールバック関数
      () => {
        console.log('success');
      }, // 成功時のコールバック関数
    );
  };

  return (
    <>
        <Button onPress={() => initializeDatabase()}>
          <Text>Initialize database</Text>
        </Button>
        <Button onPress={() => showData()}>
          <Text>Show data</Text>
        </Button>
        <Button onPress={() => deleteData()}>
          <Text>Delete data</Text>
        </Button>
    </>
  );
};

SQLite は executeSql ですべての処理を行います。

データベースの作成場所は、アプリケーションのデフォルト保存領域に保存されます。

console.log(FileSystem.documentDirectory + 'SQLite/'); でデータベースの作成場所を出力しています。

あとは基本的にコメントを付けてある通りなのですが、1点注意事項があり、select文のあとのsetItemsの直後にitemsを操作する処理を書いても、データが未設定のためデータがゼロ件になり処理が失敗します。

上記サンプルでは、ボタンを分けているのでタイミングの問題は起きないですが、select文のあとにitemsを扱いたい場合は、useEffectを使用する必要があります。イメージとしては以下のような感じですね。

useEffect(() => {

  // items を扱う処理

}, [items])

itemsの値が変わったら、itemsを扱う処理が実行されます。

おわりに

ざっくり、React Native + Expo + TypeScript で SQLite のデータベースにアクセスする方法をご紹介しました。

複数データやパラメータクエリのサンプルも掲載しているので、基本的なことはこれでできると思います。

ハマりどころが多いですが、いろいろ試してみてください。

参考サイト

qiita.com

stackoverflow.com

docs.expo.io

docs.expo.io