React v18から sqlite3 WASM(SQLite公式のWebAssembly実装)を使ってみました。
- 外部のWeb APIからデータを取得するのに、JSONPlaceholder の todos API を使用します。
そのデータ内容は下図の通りです。
アプリ実行イメージ
先にアプリ実行イメージを示しておきます。
- まず「データ取得」ボタンを押すと、todos API を呼んで、レスポンスデータをsqlite3 WASMに保存します。
- データ取得後に「表示」ボタンを押すと、sqlite3 WASMにクエリーを発行して、id列とtitle列をブラウザ画面に表示します。
参考サイト(感謝します)
ライブラリのダウンロード
- 本記事執筆時点で、npmやyarnでインストールできません。
- 公式ページ の「WebAssembly & JavaScript」からダウンロードします。
- 本記事では sqlite-wasm-3410100.zip をダウンロードし、プロジェクト配下のsrcディレクトリの下に解凍しました。
ソースコード
todoのデータ型
TypeScriptですので、必要な型を定義します。
src/types/todo.ts
export type TodoType = {
userId: number;
id: number;
title: string;
completed: boolean;
};
データ表示コンポーネント
「表示」ボタンが押されたときに、todoのid列と title列を表示するコンポーネントです。
src/Todo.tsx
import { FC } from "react"
import { TodoType } from "./types/todo"
type Props = {
todo: TodoType;
};
export const Todo: FC<Props> = (props) => {
const { todo } = props;
return <p>{`(${todo.id}) ${todo.title}`}</p>;
}
主たる処理を行うコンポーネント
- 最初に呼ばれる initDb()で、dbオブジェクトとテーブルを作成します。
- 「データ取得」ボタンが押された時に呼ばれる onClickGetTodos()で、外部の todos API を呼んで、レスポンスデータをsqlite3にINSERTします。
- 「表示」ボタンが押された時に呼ばれる onClickPrintTodos()で、sqlite3にSELECT文を発行して、結果データをブラウザ画面に表示します。
src/App.tsx
import sqlite3InitModule from "./sqlite-wasm-3410100/jswasm/sqlite3";
import { useState, useEffect, useRef } from "react";
import axios from "axios";
import { TodoType } from "./types/todo";
import { Todo } from "./Todo";
const createTableStmt = `
CREATE TABLE todos (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
completed INT NOT NULL
) STRICT`;
const insertStmt = `
INSERT INTO todos (
id,
user_id,
title,
completed
)
VALUES (
$id,
$user_id,
$title,
$completed)`;
const selectStmt = `
SELECT
id,
user_id AS userId,
title,
completed
FROM
todos`;
function App() {
const db = useRef<any | null>(null);
const [status, setMessage] = useState<string>("");
const [todos, setTodos] = useState<Array<TodoType>>([]);
const [disableButton, setDisableButton] = useState<boolean>(false);
useEffect(() => {
(async() => {
await initDb()
})();
}, []);
const initDb = async () => {
try {
const sqlite3 = await sqlite3InitModule();
const oo = sqlite3.oo1;
db.current = new oo.DB("/foo.sqlite3", "ct");
db.current.exec(createTableStmt);
}
catch (err) {
setMessage((err as Error).toString());
}
};
const onClickGetTodos = async () => {
try {
setDisableButton(true);
setTodos([]);
const res = await axios.get<Array<TodoType>>("https://jsonplaceholder.typicode.com/todos");
setMessage(`status=${res.status}`);
res.data.map((rec) => {
db.current.exec({
sql: insertStmt,
bind: {
$id: rec.id,
$user_id: rec.userId,
$title: rec.title,
$completed: rec.completed
}
});
});
}
catch (err) {
setMessage((err as Error).toString());
}
finally {
setDisableButton(false);
}
};
const onClickPrintTodos = () => {
try {
setDisableButton(true);
let resultRows: Array<TodoType> = [];
db.current.exec({
sql: selectStmt,
rowMode: "object",
resultRows: resultRows,
callback: function(row: TodoType) {
// デバッグ用
console.log(`id=${row.id}, userId=${row.userId}, completed=${row.completed}, title=${row.title}`);
}
});
setTodos(resultRows);
}
catch (err) {
setMessage((err as Error).toString());
}
finally {
setDisableButton(false);
}
};
return (
<>
<button disabled={disableButton} onClick={onClickGetTodos}>データ取得</button>
<p>{status}</p>
<button disabled={disableButton} onClick={onClickPrintTodos}>表示</button>
{todos.map((todo) => {
return <Todo key={todo.id} todo={todo} />;
})}
</>
);
}
export default App;
用途
ページネーションに対応していない外部APIを使う時に、一旦全データをsqlite3に保存しておいて、Reactでページネーション処理するのに使えそうかな?と思いました(まだ試していません)。