Storage Utilities
RNCopilot provides a typed wrapper around react-native-mmkv for persistent key-value storage. The wrapper adds a result-object pattern for error handling, a listener system for reactivity, and React hooks for component integration.
Source: src/utils/storage/
STORAGE_KEYS
All storage keys are defined as a typed constant object. Using these constants instead of raw strings prevents typos and enables IDE autocompletion.
import { STORAGE_KEYS } from '@/utils/storage';
// Available keys:
STORAGE_KEYS.preferences.theme // 'user_theme_preference'
STORAGE_KEYS.preferences.themePreset // 'user_theme_preset'
STORAGE_KEYS.preferences.language // 'user_language'
STORAGE_KEYS.preferences.onboardingCompleted // 'onboarding_completed'
STORAGE_KEYS.preferences.notificationsEnabled // 'notifications_enabled'
STORAGE_KEYS.auth.lastEmail // 'auth_last_email'
STORAGE_KEYS.app.lastVersion // 'app_last_version'
STORAGE_KEYS.app.launchCount // 'app_launch_count'The StorageKey type is derived from this object, so TypeScript enforces that only valid keys are used.
Result Object Pattern
All imperative storage functions return a StorageResult<T> object. Always check .success before using .data:
interface StorageResult<T> {
success: boolean;
data?: T;
error?: Error;
}import { getItem, STORAGE_KEYS } from '@/utils/storage';
const result = getItem<string>(STORAGE_KEYS.preferences.language);
if (result.success && result.data) {
console.log('Language:', result.data);
} else if (!result.success) {
console.error('Storage error:', result.error?.message);
}Never access .data without first checking .success. On failure, .data is undefined and .error contains the Error object.
Imperative Functions
These functions can be called anywhere — in components, hooks, services, or utility code.
getItem
function getItem<T extends StorageValue>(key: StorageKey): StorageResult<T | null>Retrieves a value by key. Returns { success: true, data: null } if the key does not exist. Automatically parses JSON strings back into objects.
setItem
function setItem<T extends StorageValue>(key: StorageKey, value: T): StorageResult<T>Stores a value. Supports string, number, boolean, and object (serialized as JSON). Passing null or undefined removes the key.
removeItem
function removeItem(key: StorageKey): StorageResult<void>Removes a key from storage.
hasItem
function hasItem(key: StorageKey): booleanReturns true if the key exists in storage.
clear
function clear(): StorageResult<void>Removes all keys from storage.
getAllKeys
function getAllKeys(): StorageResult<StorageKey[]>Returns all stored keys.
getStorageSize
function getStorageSize(): numberReturns an approximate size in bytes of all stored data.
addListener
function addListener<T extends StorageValue>(
key: StorageKey,
callback: (value: T | null) => void
): () => voidSubscribes to changes for a specific key. Returns an unsubscribe function.
initializeStorage
function initializeStorage(options?: {
id?: string;
encryptionKey?: string;
}): voidInitializes the MMKV instance. Called automatically on first access with default options. Call explicitly if you need a custom ID or encryption.
React Hooks
useStorage
A reactive hook that reads, writes, and subscribes to a storage key. Re-renders the component when the value changes.
function useStorage<T extends StorageValue>(
key: StorageKey,
options?: UseStorageOptions<T>
): UseStorageReturn<T>Options:
| Option | Type | Default | Description |
|---|---|---|---|
defaultValue | T | undefined | Value to use if key does not exist |
initializeWithDefault | boolean | false | Write defaultValue to storage if key is missing |
Returns:
| Property | Type | Description |
|---|---|---|
value | T | null | Current value |
setValue | (value: T | null) => void | Update the stored value |
removeValue | () => void | Remove the key from storage |
loading | boolean | true during initial read |
error | Error | null | Error if the last operation failed |
refresh | () => void | Re-read the value from storage |
Example:
import { useStorage, STORAGE_KEYS } from '@/utils/storage';
export function LanguagePicker() {
const { value: language, setValue: setLanguage, loading } =
useStorage<string>(STORAGE_KEYS.preferences.language, {
defaultValue: 'en',
});
if (loading) return <Loading />;
return (
<SegmentedControl
value={language}
onChange={setLanguage}
options={['en', 'ar']}
/>
);
}useStorageBoolean
A specialized version of useStorage for boolean values, with an added toggle convenience method.
function useStorageBoolean(
key: StorageKey,
options?: UseStorageOptions<boolean>
): UseStorageReturn<boolean> & { toggle: () => void }Example:
import { useStorageBoolean, STORAGE_KEYS } from '@/utils/storage';
export function NotificationToggle() {
const { value: enabled, toggle } = useStorageBoolean(
STORAGE_KEYS.preferences.notificationsEnabled,
{ defaultValue: true }
);
return (
<Switch value={enabled ?? true} onValueChange={toggle} />
);
}Supported Value Types
The StorageValue type defines what can be stored:
type StorageValue = string | number | boolean | object | null;Objects are automatically serialized to JSON on write and parsed on read.