この記事は 2024 年当時の記録です(2026 年 7 月 追記)
本文の
expo-sqliteのコードは古い API で書かれており、そのままでは動きません。expo-sqlite はその後、内部から書き直され、コールバックを使うdb.transaction()/tx.executeSql()方式は廃止されました。現在はasync / awaitベースの API になっています。SQL 文(CREATE / INSERT / SELECT / UPDATE / DELETE)自体は同じなので、呼び出し方を新しい形に置き換えれば、本文の考え方はそのまま使えます。新しい API の対応表
SQLite.openDatabase("foo.db")→await SQLite.openDatabaseAsync("foo.db")db.transaction((tx) => {...})→ 廃止。各操作を直接awaitで呼ぶ- 書き込み(INSERT / UPDATE / DELETE)→
await db.runAsync(...)- 複数行の取得(SELECT)→
await db.getAllAsync(...)- 1 行だけ取得 →
await db.getFirstAsync(...)- まとめて実行 →
await db.execAsync(...)本文の
addItemを新しい書き方にすると、次のようになります。import * as SQLite from "expo-sqlite"; // データベースを開く(一度だけ) const db = await SQLite.openDatabaseAsync("foo.db"); // テーブル作成 await db.execAsync( "CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY NOT NULL, done INT, value TEXT);" ); // 追加(INSERT) const addItem = async (text) => { if (!text) return false; await db.runAsync("INSERT INTO items (done, value) VALUES (0, ?)", text); const rows = await db.getAllAsync("SELECT * FROM items"); console.log(rows); }; // 更新(UPDATE) const updateItem = async (id) => { await db.runAsync("UPDATE items SET done = 1 WHERE id = ?", id); }; // 削除(DELETE) const deleteItem = async (id) => { await db.runAsync("DELETE FROM items WHERE id = ?", id); };本文の
useForceUpdate()で強制的に再描画する手法も不要になりました。async / awaitで取得した結果をuseStateにそのまま入れれば画面が更新されます。また、SQLiteProviderとuseSQLiteContext()を使うと、コンポーネント間でデータベースを共有しやすくなります。詳しくは expo-sqlite 公式ドキュメントをご確認ください。※ 上記は公式ドキュメントに基づく整理であり、新しい環境での再検証は行っていません。実際に実装される際は公式ドキュメントをご確認ください。
expo-sqlite を使用すると、WebSQL のような API を通じてクエリできるデータベースにアプリがアクセスできるようになります。 データベースはアプリを再起動しても保持されます。
開発環境(Expo Bare)
- “typescript”: “^5.2.2”
- “expo”: “~49.0.13”,
- “react”: “18.2.0”,
- “react-native”: “0.72.6”,
- “expo-router”: “^2.0.0”,
- “react-native-paper”: “^5.10.6”,
- “expo-sqlite“: “~11.3.3”,
セットアップする
.db 拡張子を認識させる
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.resolver.assetExts.push('db'); // <-- 追加する
module.exports = defaultConfig;
expo-sqlite
パッケージを設置する
npx expo install expo-sqlite npx pod-install
フック(Hooks)を作成する
SQLiteの使用上、SQL Queryを実行する際、コンポネートのレンダリング(re-render)を手動で行う必要がありますので、useForceUpdateフックを作成しておきます。
export function useForceUpdate() {
const [value, setValue] = React.useState(0);
return () => setValue(value + 1);
}
データを処理する(CRUD+)
データベースをオープンする
import * as SQLite from "expo-sqlite";
function openDatabase() {
const db = SQLite.openDatabase("foo.db"); // データベース名(foo.db)は任意
return db;
}
const db = openDatabase()
export default function RootLayout() {
...
CREATE TABLE …
useEffect(() => {
db.transaction((tx) => {
tx.executeSql(
"CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT, done INT, value TEXT);"
);
});
}, []);
INSERT INTO …
SELECT <カラム名> FROM …
const forceUpdate = useForceUpdate();
const addItem = (text) => {
if (text === null || text === "") {
return false;
}
db.transaction(
(tx) => {
tx.executeSql("INSERT INTO items (done, value) values (0, ?)", [text]);
tx.executeSql("SELECT * FROM items", [], (_, { rows }) =>
console.log(JSON.stringify(rows))
);
},
undefined,
forceUpdate
);
};
UPDATE <テーブル名> SET …
const forceUpdate = useForceUpdate();
const updateItem = (id) => {
db.transaction(
(tx) => {
tx.executeSql("UPDATE items SET done = 1 WHERE id = ?;", [id]);
},
undefined,
forceUpdate
);
};
DELETE FROM …
const forceUpdate = useForceUpdate();
const deleteItem = (id) => {
db.transaction(
(tx) => {
tx.executeSql("DELETE FROM items WHERE id = ?;", [id]);
},
undefined,
forceUpdate
);
};
DROP TABLE …
const forceUpdate = useForceUpdate();
const dropTable = (id) => {
db.transaction(
(tx) => {
tx.executeSql("DROP TABLE IF EXISTS items ");
},
undefined,
forceUpdate
);
};
リリースする
※上記の詳細は以下のアプリに実装されています!
この SQLite の実装は、以下のアプリで使っています。実際の動作は各ストアからご確認いただけます。
Kotango(ハングル単語)
hajimari(自分だけのはじまりを記録する)


コメント