Migrate storage from localStorage to IndexedDB using localForage#2102
Migrate storage from localStorage to IndexedDB using localForage#2102RodriSanchez1 merged 6 commits intomasterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates Redux persisted state storage from localStorage to IndexedDB by switching redux-persist’s storage engine to localForage, while providing a wrapper that can transparently migrate existing persisted keys from the legacy storage on first read.
Changes:
- Add
localforagedependency and lockfile entries. - Configure
localForageto use acboardIndexedDB store. - Replace redux-persist storage with a migrating storage wrapper that reads from IndexedDB first and migrates from legacy localStorage when needed.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
package.json |
Adds localforage dependency required for IndexedDB-backed persistence. |
yarn.lock |
Locks localforage and its transitive dependency versions. |
src/reducers.js |
Introduces migrating storage wrapper and switches redux-persist configs to use it. |
src/reducers.js
Outdated
| const createMigratingStorage = (oldStorage, newStorage) => ({ | ||
| /** | ||
| * Retrieves a value from storage, migrating from old to new if necessary. | ||
| * Called by redux-persist on app initialization. | ||
| */ | ||
| async getItem(key) { | ||
| try { | ||
| const newValue = await newStorage.getItem(key); | ||
| if (newValue !== null && newValue !== undefined) { | ||
| return newValue; | ||
| } | ||
| } catch (err) { | ||
| console.warn('Cboard: IndexedDB read failed', err); | ||
| } | ||
|
|
||
| try { | ||
| const oldValue = await oldStorage.getItem(key); | ||
| if (oldValue !== null && oldValue !== undefined) { | ||
| console.log( | ||
| `Cboard: Migrating ${key} from localStorage to IndexedDB...` | ||
| ); | ||
| try { | ||
| await newStorage.setItem(key, oldValue); | ||
| console.log(`Cboard: Successfully migrated ${key}`); | ||
| await oldStorage.removeItem(key); | ||
| console.log(`Cboard: Cleaned up ${key} from localStorage`); | ||
| } catch (writeErr) { | ||
| console.warn('Cboard: Migration write failed', writeErr); | ||
| } | ||
| return oldValue; | ||
| } |
There was a problem hiding this comment.
The new migrating storage wrapper introduces important behavior (read-from-new, fallback-to-old, one-time copy + cleanup). There are currently only smoke tests for createReducer, so this migration path isn’t covered. Add Jest tests that verify (1) when only legacy storage has persist:*, getItem copies to localForage and removes from legacy storage, and (2) when localForage already has the key, legacy storage is not read/modified.
Storage Migration: localStorage to IndexedDB
Overview
This document describes the storage migration implemented in Cboard to move from
localStoragetoIndexedDBfor persisting Redux state.Problem
Users with many boards were encountering
QuotaExceededErrorbecause localStorage has a strict ~5-10MB limit per origin. When users created many boards with images and tiles, they would exceed this limit and lose the ability to save new data.Solution
Migrate to IndexedDB via localForage, which supports gigabytes of storage depending on the browser.
Implementation
The migration is implemented in src/reducers.js using a storage wrapper pattern.
How It Works
Storage Wrapper
The migration uses a custom storage wrapper that implements the redux-persist storage interface:
getItem(key)setItem(key, value)removeItem(key)Persisted Keys
The app persists two separate keys:
persist:rootpersist:languageBoth keys are migrated automatically by the storage wrapper.
Edge Cases Handled
Safari Private Browsing Mode
IndexedDB is not persistent in Safari private mode. localForage automatically detects this and uses localStorage as its driver.
New Users
For new users (no data in either storage),
getItemreturnsnulland redux-persist initializes with default state.Already Migrated Users
On subsequent visits, data is found in IndexedDB immediately. localStorage is never checked, so there's no performance overhead.
Cordova/Mobile App
localForage works in Cordova environments and automatically selects the best available storage backend.
Multi-Tab Usage
IndexedDB is designed for multi-tab access. Data is consistent across tabs.
Verifying the Migration
Check IndexedDB in DevTools
persist:rootandpersist:languagekeysConsole Messages
During migration, you'll see these messages in the console:
After migration, these messages won't appear on subsequent page loads.
Rollback
If issues are discovered, revert to localStorage by changing the storage config in
src/reducers.js:Note: After migration, old localStorage data is cleaned up. Users who have already migrated will have their data in localForage (IndexedDB) only.
Dependencies
References
Related Issues
close #1514