react-native-mmkv
Version:
The fastest key/value storage for React Native. ~30x faster than AsyncStorage! Works on Android, iOS and Web.
177 lines (169 loc) • 5.46 kB
JavaScript
import { useRef, useState, useMemo, useCallback, useEffect } from 'react';
import { MMKV } from './MMKV';
function isConfigurationEqual(left, right) {
if (left == null || right == null) return left == null && right == null;
return left.encryptionKey === right.encryptionKey && left.id === right.id && left.path === right.path;
}
let defaultInstance = null;
function getDefaultInstance() {
if (defaultInstance == null) {
defaultInstance = new MMKV();
}
return defaultInstance;
}
/**
* Use the default, shared MMKV instance.
*/
export function useMMKV(configuration) {
const instance = useRef();
const lastConfiguration = useRef();
if (configuration == null) return getDefaultInstance();
if (instance.current == null || !isConfigurationEqual(lastConfiguration.current, configuration)) {
lastConfiguration.current = configuration;
instance.current = new MMKV(configuration);
}
return instance.current;
}
function createMMKVHook(getter) {
return (key, instance) => {
const mmkv = instance ?? getDefaultInstance();
const [value, setValue] = useState(() => getter(mmkv, key));
const valueRef = useRef(value);
valueRef.current = value;
// update value by user set
const set = useCallback(v => {
const newValue = typeof v === 'function' ? v(valueRef.current) : v;
switch (typeof newValue) {
case 'number':
case 'string':
case 'boolean':
mmkv.set(key, newValue);
break;
case 'undefined':
mmkv.delete(key);
break;
case 'object':
if (newValue instanceof Uint8Array) {
mmkv.set(key, newValue);
break;
} else {
throw new Error(`MMKV: Type object (${newValue}) is not supported!`);
}
default:
throw new Error(`MMKV: Type ${typeof newValue} is not supported!`);
}
}, [key, mmkv]);
// update value if key or instance changes
useEffect(() => {
setValue(getter(mmkv, key));
}, [key, mmkv]);
// update value if it changes somewhere else (second hook, same key)
useEffect(() => {
const listener = mmkv.addOnValueChangedListener(changedKey => {
if (changedKey === key) {
setValue(getter(mmkv, key));
}
});
return () => listener.remove();
}, [key, mmkv]);
return [value, set];
};
}
/**
* Use the string value of the given `key` from the given MMKV storage instance.
*
* If no instance is provided, a shared default instance will be used.
*
* @example
* ```ts
* const [username, setUsername] = useMMKVString("user.name")
* ```
*/
export const useMMKVString = createMMKVHook((instance, key) => instance.getString(key));
/**
* Use the number value of the given `key` from the given MMKV storage instance.
*
* If no instance is provided, a shared default instance will be used.
*
* @example
* ```ts
* const [age, setAge] = useMMKVNumber("user.age")
* ```
*/
export const useMMKVNumber = createMMKVHook((instance, key) => instance.getNumber(key));
/**
* Use the boolean value of the given `key` from the given MMKV storage instance.
*
* If no instance is provided, a shared default instance will be used.
*
* @example
* ```ts
* const [isPremiumAccount, setIsPremiumAccount] = useMMKVBoolean("user.isPremium")
* ```
*/
export const useMMKVBoolean = createMMKVHook((instance, key) => instance.getBoolean(key));
/**
* Use the buffer value (unsigned 8-bit (0-255)) of the given `key` from the given MMKV storage instance.
*
* If no instance is provided, a shared default instance will be used.
*
* @example
* ```ts
* const [privateKey, setPrivateKey] = useMMKVBuffer("user.privateKey")
* ```
*/
export const useMMKVBuffer = createMMKVHook((instance, key) => instance.getBuffer(key));
/**
* Use an object value of the given `key` from the given MMKV storage instance.
*
* If no instance is provided, a shared default instance will be used.
*
* The object will be serialized using `JSON`.
*
* @example
* ```ts
* const [user, setUser] = useMMKVObject<User>("user")
* ```
*/
export function useMMKVObject(key, instance) {
const [string, setString] = useMMKVString(key, instance);
const value = useMemo(() => {
if (string == null) return undefined;
return JSON.parse(string);
}, [string]);
const setValue = useCallback(v => {
if (v == null) {
// Clear the Value
setString(undefined);
} else {
// Store the Object as a serialized Value
setString(JSON.stringify(v));
}
}, [setString]);
return [value, setValue];
}
/**
* Listen for changes in the given MMKV storage instance.
* If no instance is passed, the default instance will be used.
* @param valueChangedListener The function to call whenever a value inside the storage instance changes
* @param instance The instance to listen to changes to (or the default instance)
*
* @example
* ```ts
* useMMKVListener((key) => {
* console.log(`Value for "${key}" changed!`)
* })
* ```
*/
export function useMMKVListener(valueChangedListener, instance) {
const ref = useRef(valueChangedListener);
ref.current = valueChangedListener;
const mmkv = instance ?? getDefaultInstance();
useEffect(() => {
const listener = mmkv.addOnValueChangedListener(changedKey => {
ref.current(changedKey);
});
return () => listener.remove();
}, [mmkv]);
}
//# sourceMappingURL=hooks.js.map