IndexedDB でアプリケーションの状態を保持するためのベスト プラクティス

IndexedDB と一般的な状態管理ライブラリ間でアプリケーションの状態を同期するためのベスト プラクティスを学びます。

ユーザーがウェブサイトまたはアプリを初めて読み込むとき、UI のレンダリングに使用される初期のアプリケーション状態の構築に多くの作業が伴うことがよくあります。たとえば、ページに表示する必要があるすべてのデータを取得するために、アプリがクライアントサイドでユーザーを認証し、その後に複数の API リクエストを行う必要がある場合があります。

アプリケーションの状態を IndexedDB に保存すると、再訪時の読み込み時間を短縮できます。アプリは、バックグラウンドで任意の API サービスと同期し、stale-while-revalidate 戦略を使用して、新しいデータで UI を遅延更新できます。

ただし、IndexedDB を使用する場合は、API を初めて使用するデベロッパーにはすぐにはわからない重要な考慮事項が多数あります。このドキュメントでは、よくある質問に回答し、IndexedDB でアプリケーションの状態を保持する際に考慮すべき最も重要な点について説明します。

アプリの予測可能性を維持する

IndexedDB の複雑さの多くは、デベロッパーが制御できない要素が非常に多いという事実に起因しています。このセクションでは、IndexedDB を使用する際に考慮すべき多くの問題について説明します。

ストレージへの書き込みが失敗する可能性がある

IndexedDB への書き込み時にエラーが発生する原因はさまざまですが、その原因がデベロッパーの管理範囲外である場合もあります。たとえば、一部のブラウザでは、シークレット モードで IndexedDB への書き込みが許可されていません。また、ディスク容量がほとんど残っていないデバイスを使用しているユーザーの場合、ブラウザで何も保存できない可能性があります。

そのため、IndexedDB コードに適切なエラー処理を常に実装することが重要です。また、一般的に、(保存に加えて)アプリケーションの状態をメモリに保持しておくと、プライベート ブラウジング モードで実行しているときやストレージ スペースが使用できないときに UI が破損するのを防ぐことができます(ストレージを必要とする他のアプリ機能の一部が機能しない場合でも同様です)。

保存されているデータがユーザーによって変更または削除されている可能性があります

不正なアクセスを制限できるサーバーサイド データベースとは異なり、クライアントサイド データベースにはブラウザ拡張機能とデベロッパー ツールからアクセスでき、ユーザーが消去できます。

ユーザーがローカルに保存されているデータを変更することはあまりありませんが、データを消去することはよくあります。アプリケーションがエラーが発生することなく、これらの両方のケースを処理できるようにすることが重要です。

保存されているデータが古い可能性があります

前のセクションと同様に、ユーザーがデータを変更していなくても、ストレージに保存されているデータが以前のバージョンのコード(バグのあるバージョンの場合もあります)によって書き込まれている可能性があります。

IndexedDB には、スキーマ バージョンと、IDBOpenDBRequest.onupgradeneeded() メソッドを使用したアップグレードが組み込まれています。ただし、以前のバージョン(バグのあるバージョンを含む)からアクセスしたユーザーを処理できるように、アップグレード コードを記述する必要があります。

単体テストは、考えられるすべてのアップグレード パスとケースを手動でテストすることが現実的でないことが多いため、ここで非常に役立ちます。

アプリのパフォーマンスを維持する

IndexedDB の重要な機能の一つは非同期 API ですが、使用時にパフォーマンスを気にする必要がないと考えるのは誤りです。不適切な使用によってメインスレッドがブロックされ、応答しなくなるケースは数多くあります。

一般に、IndexedDB への読み取りと書き込みは、アクセスされるデータに必要なサイズを超えないようにする必要があります。

IndexedDB では、大きなネストされたオブジェクトを 1 つのレコードとして保存できます(これはデベロッパーにとって非常に便利ですが)。ただし、この方法は避けてください。これは、IndexedDB がオブジェクトを保存するときに、まずそのオブジェクトの構造化クローンを作成する必要があり、構造化クローン作成プロセスがメインスレッドで行われるためです。オブジェクトが大きいほど、ブロック時間は長くなります。

一般的な状態管理ライブラリ(Redux など)のほとんどは、状態ツリー全体を単一の JavaScript オブジェクトとして管理するため、IndexedDB にアプリケーションの状態を保持する方法の計画には課題があります。

この方法で状態を管理することには多くの利点があり、状態ツリー全体を IndexedDB に 1 つのレコードとして保存することは魅力的で便利ですが、変更のたびに(スロットリングまたはデバウンスされている場合でも)これを行うと、メインスレッドが不必要にブロックされ、書き込みエラーが発生する可能性が高くなります。場合によっては、ブラウザのタブがクラッシュしたり、応答しなくなったりすることもあります。

状態ツリー全体を 1 つのレコードに格納するのではなく、個々のレコードに分割し、実際に変更されたレコードのみを更新する必要があります。

ほとんどのベスト プラクティスと同様に、これは「すべてまたは何も」のルールではありません。状態オブジェクトを分割して最小の変更セットのみを書き込むことが現実的でない場合は、状態ツリー全体を常に書き込むよりも、データをサブツリーに分割してそれらのみを書き込むことをお勧めします。少しでも改善したほうが、何も改善しないよりはましです。

最後に、作成するコードのパフォーマンスへの影響を常に測定する必要があります。IndexedDB への小さな書き込みは大きな書き込みよりもパフォーマンスが高いのは事実ですが、これは、アプリが行っている IndexedDB への書き込みが、メインスレッドをブロックしてユーザー エクスペリエンスを低下させる長いタスクに実際につながる場合にのみ重要です。測定を行うことで、何を最適化しているかを把握することが重要です。

まとめ

IndexedDB などのクライアント ストレージ メカニズムを使用すると、セッション間で状態を保持するだけでなく、再訪時に初期状態を読み込む時間を短縮することで、アプリケーションのユーザー エクスペリエンスを改善できます。

IndexedDB を正しく使用するとユーザー エクスペリエンスが大幅に向上しますが、誤って使用したり、エラーケースを処理できなかったりすると、アプリが破損し、ユーザーの不満につながる可能性があります。

クライアント ストレージには、制御できない多くの要因が関係するため、コードを十分にテストし、最初は発生しそうにないエラーであっても適切に処理することが重要です。