# Preference Usage Guide This guide covers how to use the Preference system in React components and services. ## React Hooks ### usePreference (Single Preference) ```typescript import { usePreference } from '@data/hooks/usePreference' // Basic usage - optimistic updates (default) const [theme, setTheme] = usePreference('app.theme.mode') // Update the value await setTheme('dark') // With pessimistic updates (wait for confirmation) const [apiKey, setApiKey] = usePreference('api.key', { optimistic: false }) ``` ### usePreferences (Multiple Preferences) ```typescript import { usePreferences } from '@data/hooks/usePreference' // Read multiple preferences at once const { theme, language, fontSize } = usePreferences([ 'app.theme.mode', 'app.language', 'chat.message.font_size' ]) ``` ## Update Strategies ### Optimistic Updates (Default) UI updates immediately, then syncs to database. Automatic rollback on failure. ```typescript const [theme, setTheme] = usePreference('app.theme.mode') const handleThemeChange = async (newTheme: string) => { try { await setTheme(newTheme) // UI updates immediately } catch (error) { // UI automatically rolls back console.error('Theme update failed:', error) } } ``` **Best for:** - Frequent changes (theme, font size) - Non-critical settings - Better perceived performance ### Pessimistic Updates Waits for database confirmation before updating UI. ```typescript const [apiKey, setApiKey] = usePreference('api.key', { optimistic: false }) const handleApiKeyChange = async (newKey: string) => { try { await setApiKey(newKey) // Waits for DB confirmation toast.success('API key saved') } catch (error) { toast.error('Failed to save API key') } } ``` **Best for:** - Security-sensitive settings (API keys, passwords) - Settings that affect external services - When confirmation feedback is important ## PreferenceService Direct Usage For non-React code or batch operations. ### Renderer Process ```typescript import { preferenceService } from '@data/PreferenceService' // Get single preference const theme = await preferenceService.get('app.theme.mode') // Get multiple preferences const settings = await preferenceService.getMultiple([ 'app.theme.mode', 'app.language' ]) // Returns: { 'app.theme.mode': 'dark', 'app.language': 'en' } // Get with default value const fontSize = await preferenceService.get('chat.message.font_size') ?? 14 ``` ### Set Preferences ```typescript // Set single preference (optimistic by default) await preferenceService.set('app.theme.mode', 'dark') // Set with pessimistic update await preferenceService.set('api.key', 'secret', { optimistic: false }) // Set multiple preferences at once await preferenceService.setMultiple({ 'app.theme.mode': 'dark', 'app.language': 'en', 'chat.message.font_size': 16 }) ``` ### Subscribe to Changes ```typescript // Subscribe to preference changes (useful in services) const unsubscribe = preferenceService.subscribe('app.theme.mode', (newValue) => { logger.info('Theme changed to:', { theme: newValue }) }) // Cleanup when done unsubscribe() ``` ### Main Process In the main process, PreferenceService is lifecycle-managed. Access it via `application.get()`: ```typescript import { application } from '@application' const preferenceService = application.get('PreferenceService') // Get/set preferences const theme = preferenceService.get('app.theme.mode') preferenceService.set('app.theme.mode', 'dark') // Subscribe to changes const unsubscribe = preferenceService.subscribeChange('app.theme.mode', (newValue) => { logger.info('Theme changed to:', { theme: newValue }) }) ``` > **Note:** Do NOT import PreferenceService directly in main process code. The renderer `@data/PreferenceService` is a separate singleton for the renderer process only. ## Common Patterns ### Settings Form ```typescript function SettingsForm() { const [theme, setTheme] = usePreference('app.theme.mode') const [language, setLanguage] = usePreference('app.language') const [fontSize, setFontSize] = usePreference('chat.message.font_size') return (
setFontSize(Number(e.target.value))} min={12} max={24} />
) } ``` ### Feature Toggle ```typescript function ChatMessage({ message }) { const [showTimestamp] = usePreference('chat.display.show_timestamp') return (

{message.content}

{showTimestamp && {message.createdAt}}
) } ``` ### Conditional Rendering Based on Settings ```typescript function App() { const [theme] = usePreference('app.theme.mode') const [sidebarPosition] = usePreference('app.sidebar.position') return (
{sidebarPosition === 'left' && } {sidebarPosition === 'right' && }
) } ``` ### Batch Settings Update ```typescript async function resetToDefaults() { await preferenceService.setMultiple({ 'app.theme.mode': 'system', 'app.language': 'en', 'chat.message.font_size': 14, 'chat.display.show_timestamp': true }) } ``` ## Adding New Preference Keys For a complete guide on adding new preferences, including naming conventions and design principles, see [Preference Schema Guide](./preference-schema-guide.md). Quick summary: 1. Add custom types to `preferenceTypes.ts` (if needed) 2. Add key to `PreferenceSchemas` interface in `preferenceSchemas.ts` 3. Add default value to `DefaultPreferences` 4. Use in code with full type safety ## Best Practices 1. **Choose update strategy wisely**: Optimistic for UX, pessimistic for critical settings 2. **Batch related updates**: Use `setMultiple` when changing multiple related settings 3. **Provide sensible defaults**: All preferences should have default values 4. **Keep values atomic**: One preference = one logical setting 5. **Use consistent naming**: Follow `domain.feature.setting` pattern ## Preference vs Other Storage | Scenario | Use | |----------|-----| | User theme preference | `usePreference('app.theme.mode')` | | Window position | `usePersistCache` (can be lost without impact) | | API key | `usePreference` with pessimistic updates | | Search history | `usePersistCache` (nice to have) | | Conversation history | `DataApiService` (business data) |