UNPKG

react-native-mmkv

Version:

The fastest key/value storage for React Native. ~30x faster than AsyncStorage! Works on Android, iOS and Web.

128 lines (114 loc) 3.76 kB
/* global localStorage */ import type { Configuration, NativeMMKV } from './Types'; import { createTextEncoder } from './createTextEncoder'; const canUseDOM = typeof window !== 'undefined' && window.document?.createElement != null; const hasAccessToLocalStorage = () => { try { // throws ACCESS_DENIED error window.localStorage; return true; } catch { return false; } }; const KEY_WILDCARD = '\\'; const inMemoryStorage = new Map<string, string>(); export const createMMKV = (config: Configuration): NativeMMKV => { if (config.encryptionKey != null) { throw new Error("MMKV: 'encryptionKey' is not supported on Web!"); } if (config.path != null) { throw new Error("MMKV: 'path' is not supported on Web!"); } // canUseDOM check prevents spam in Node server environments, such as Next.js server side props. if (!hasAccessToLocalStorage() && canUseDOM) { console.warn( 'MMKV: LocalStorage has been disabled. Your experience will be limited to in-memory storage!' ); } const storage = () => { if (!canUseDOM) { throw new Error( 'Tried to access storage on the server. Did you forget to call this in useEffect?' ); } if (!hasAccessToLocalStorage()) { return { getItem: (key: string) => inMemoryStorage.get(key) ?? null, setItem: (key: string, value: string) => inMemoryStorage.set(key, value), removeItem: (key: string) => inMemoryStorage.delete(key), clear: () => inMemoryStorage.clear(), length: inMemoryStorage.size, key: (index: number) => Object.keys(inMemoryStorage).at(index) ?? null, } as Storage; } const domStorage = global?.localStorage ?? window?.localStorage ?? localStorage; if (domStorage == null) { throw new Error(`Could not find 'localStorage' instance!`); } return domStorage; }; const textEncoder = createTextEncoder(); if (config.id.includes(KEY_WILDCARD)) { throw new Error( 'MMKV: `id` cannot contain the backslash character (`\\`)!' ); } const keyPrefix = `${config.id}${KEY_WILDCARD}`; // mmkv.default\\ const prefixedKey = (key: string) => { if (key.includes('\\')) { throw new Error( 'MMKV: `key` cannot contain the backslash character (`\\`)!' ); } return `${keyPrefix}${key}`; }; return { clearAll: () => { const keys = Object.keys(storage()); for (const key of keys) { if (key.startsWith(keyPrefix)) { storage().removeItem(key); } } }, delete: (key) => storage().removeItem(prefixedKey(key)), set: (key, value) => { storage().setItem(prefixedKey(key), value.toString()); }, getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined, getNumber: (key) => { const value = storage().getItem(prefixedKey(key)); if (value == null) return undefined; return Number(value); }, getBoolean: (key) => { const value = storage().getItem(prefixedKey(key)); if (value == null) return undefined; return value === 'true'; }, getBuffer: (key) => { const value = storage().getItem(prefixedKey(key)); if (value == null) return undefined; return textEncoder.encode(value).buffer; }, getAllKeys: () => { const keys = Object.keys(storage()); return keys .filter((key) => key.startsWith(keyPrefix)) .map((key) => key.slice(keyPrefix.length)); }, contains: (key) => storage().getItem(prefixedKey(key)) != null, recrypt: () => { throw new Error('`recrypt(..)` is not supported on Web!'); }, size: 0, isReadOnly: false, trim: () => { // no-op }, }; };