Persistence & Caching
Persistence & Caching
Section titled “Persistence & Caching”Dice Chess Trainer uses localStorage to improve the user experience by persisting preferences and caching data for instant loading. We follow the Stale-While-Revalidate pattern for dynamic data.
LocalStorage Usage
Section titled “LocalStorage Usage”We use localStorage for two main purposes:
- User Preferences: Storing settings that should survive page reloads and app restarts.
- State Persistence: Storing UI states like active filters to maintain context across sessions.
Preferences Store
Section titled “Preferences Store”The PreferencesStore (src/lib/preferencesStore.svelte.ts) manages global settings:
preferredMode: The default view when opening the app (‘view’, ‘train’, ‘bookmarks’).gamesPerPage: Pagination limit for game lists.archiveOnFinish: Whether to automatically archive games after training.
Filter Persistence
Section titled “Filter Persistence”In the GameListStore (src/lib/gameListStore.svelte.ts), current filters are saved whenever they are applied. This ensures that if a user navigates away or refreshes the page, their search criteria are preserved.
Caching Strategy
Section titled “Caching Strategy”To overcome latency and provide a “snappy” UI, we implement caching for expensive data fetches.
Stale-While-Revalidate
Section titled “Stale-While-Revalidate”The Stale-While-Revalidate pattern allows us to show data immediately while keeping it fresh:
- Initial Load: On store initialization, we check
localStoragefor a cached version of the data. - Instant Render: If cache exists, we populate the UI immediately with this “stale” data.
- Background Refresh: Simultaneously, we trigger an async API request to fetch the latest data (“revalidate”).
- UI Update: Once the API response arrives, we update the store state and overwrite the cache.
Smart Cache Matching
Section titled “Smart Cache Matching”We apply this pattern to the first page of the game lists with an additional layer of verification called Smart Cache:
- We cache the first page (offset 0) for each tab:
active,archived, andfavorites. - Stored Payload: The cache includes both the
itemsand thefiltersused to fetch them. - Verification: On restoration, the store compares the current filters with the cached filters. Data is only displayed instantly if they match exactly.
- Cache Keys:
gameListCache_active,gameListCache_archived,gameListCache_favorites.
This approach ensures that if a user frequently views a specific subset of games (e.g., “Expensive Games”), that view loads instantly upon app restart, while also preventing the “flash” of incorrect data if filters have changed.
Implementation Details
Section titled “Implementation Details”Helper Methods
Section titled “Helper Methods”When implementing persistence, use the helper methods provided in src/lib/preferencesStore.svelte.ts to handle localStorage safely (especially for Server-Side Rendering or environments where localStorage might be restricted):
function getStoredValue(key: string): string | null { try { return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null; } catch { return null; }}Best Practices
Section titled “Best Practices”- Be Selective: Only cache data that significantly improves the perceived performance (e.g., the first page of a list). Avoid caching large amounts of data to prevent
localStoragebloat (limit is typically 5MB). - Handle Errors: Always wrap
JSON.parseandlocalStorageaccess intry-catchblocks. - Clear on Logout: (Planned) Ensure sensitive or user-specific cache is cleared when the user logs out.
- Test Coverage: Write unit tests to verify that data is correctly read from and written to a mocked
localStorage.