IndexedDBを活用したページ状態の保存、復元、削除

Webアプリケーションにおいて、ユーザーのアクションや設定に基づきページの状態を保存、復元、または削除することは頻繁に求められる要件となっています。この記事では、そのようなタスクをIndexedDBを利用して実現する方法と、実装時の注意点について解説します。

# 1. IndexedDBとは？

IndexedDBは、ブラウザ内での大量データの保存を目的とした低レベルAPIです。これはオブジェクトベースのデータベースで、オブジェクトを直接保存することが可能です。

# 2. ページ状態の保存、復元、削除

以下のコードは、ページのURLをキーとしてデータを保存、読み込み、または削除する手法を示しています。

## Javascript:

```javascript:script.js
const PageState = {
  db: null,
  init() {
    const request = indexedDB.open("myDatabase", 1);
    request.onupgradeneeded = (event) => {
      this.db = event.target.result;
      const objectStore = this.db.createObjectStore("pageState", {
        keyPath: "url",
      });
    };
    request.onsuccess = (event) => {
      this.db = event.target.result;
    };
  },
  saveState(data) {
    const transaction = this.db.transaction(["pageState"], "readwrite");
    const objectStore = transaction.objectStore("pageState");
    objectStore.put({ url: window.location.href, data });
  },
  loadState() {
    const transaction = this.db.transaction(["pageState"]);
    const objectStore = transaction.objectStore("pageState");
    const request = objectStore.get(window.location.href);
    return new Promise((resolve, reject) => {
      request.onsuccess = (event) => {
        resolve(request.result ? request.result.data : null);
      };
      request.onerror = (event) => {
        reject(new Error("Data retrieval failed"));
      };
    });
  },
  deleteState() {
    const transaction = this.db.transaction(["pageState"], "readwrite");
    const objectStore = transaction.objectStore("pageState");
    const request = objectStore.delete(window.location.href);
    return new Promise((resolve, reject) => {
      request.onsuccess = (event) => {
        resolve(true);
      };
      request.onerror = (event) => {
        reject(new Error("Data deletion failed"));
      };
    });
  },
};

// 初期化
PageState.init();


```

## HTML:

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IndexedDB Sample with URL as Key</title>
</head>

<body>
    <div>
        <input type="text" id="dataInput" placeholder="Enter some data">
        <!-- PageStateオブジェクトを通じて関数を呼び出す -->
        <button onclick="PageState.saveState(document.getElementById('dataInput').value)">Save</button>
        <button onclick="retrieveData()">Load</button>
        <!-- 以下が追加されたdeleteStateメソッドを呼び出すボタンです -->
        <button onclick="deleteData()">Delete</button>
    </div>
    <div id="output"></div>
    <script src="script.js"></script>
    <script>
        // loadStateの返り値を使用してデータを取得
        async function retrieveData() {
            try {
                const data = await PageState.loadState();
                document.getElementById('output').innerText = data || "No data found";
            } catch (error) {
                console.error("Error retrieving data:", error);
            }
        }

        // 以下が追加されたdeleteStateメソッドを呼び出す関数です
        async function deleteData() {
            try {
                await PageState.deleteState();
                document.getElementById('output').innerText = "Data deleted successfully";
            } catch (error) {
                console.error("Error deleting data:", error);
            }
        }
    </script>
</body>

</html>
```

# 3. データの保持期間の追加

Webアプリケーションでは、データの保持期間を設定することが求められる場面があります。IndexedDBを使用して、データの保存時に現在の日時を保存し、その日時を基にしてデータの有効期限を判定することができます。

## データの保存時に現在の日時を追加:
データを保存する際に、現在の日時も一緒に保存します。

```javascript
saveState(data) {
    const transaction = this.db.transaction(["pageState"], "readwrite");
    const objectStore = transaction.objectStore("pageState");
    const now = new Date().getTime(); // 現在の日時を取得
    objectStore.put({ url: window.location.href, data, timestamp: now });
}
```

## データの読み込み時に日時を確認して古いデータを削除:
データを読み込む際に、保存された日時を確認し、指定した期間を超えている場合はそのデータを削除します。

```javascript
loadState() {
    const transaction = this.db.transaction(["pageState"]);
    const objectStore = transaction.objectStore("pageState");
    const request = objectStore.get(window.location.href);
    return new Promise((resolve, reject) => {
        request.onsuccess = (event) => {
            const now = new Date().getTime();
            const oneDay = 24 * 60 * 60 * 1000; // 24時間をミリ秒で表現
            if (request.result && (now - request.result.timestamp) <= oneDay) {
                resolve(request.result.data);
            } else {
                this.deleteState(); // 期間を超えているデータを削除
                resolve(null);
            }
        };
        request.onerror = (event) => {
            reject(new Error("Data retrieval failed"));
        };
    });
}
```

上記の変更を加えることで、データの保持期間を1日とし、それを超えたデータは自動的に削除されるようになります。必要に応じて保持期間を調整することができます。

# 4. 考慮すべき問題点

- **エラーハンドリング**: データベースのオープンやデータの操作時のエラーハンドリングは欠かせません。
- **データベースのバージョン管理**:  バージョンが変わった際の適切な処理が必要です。
- **容量制限**: IndexedDBの容量制限を超えるとエラーが発生しますので、この制限を意識してください。
- **セキュリティ**: 機密情報を保存する場合、データの暗号化などのセキュリティ対策が必要です。
- **ブラウザの互換性**: すべてのブラウザでIndexedDBがサポートされているわけではないため、互換性を確認することが大切です。

# 5. まとめ

IndexedDBは、ブラウザ内でのデータの効率的な保存、復元、削除を実現する強力なツールです。しかし、その実装には上記のような多くの注意点が伴います。これらの要点をしっかりと把握しながら、ページの状態管理を適切に行うことが求められます。

# 6. 参考リンク

詳しいコード例や動作デモを確認したい方は、以下のGitHubリポジトリをご参照ください：

- [IndexedDBサンプルコード](https://github.com/blue-islands/IndexedDB-sample)