Skip to content

Instantly share code, notes, and snippets.

@bureyburey
Last active May 9, 2024 15:24
Show Gist options
  • Save bureyburey/2345dfa88a31e00a514479be37848d42 to your computer and use it in GitHub Desktop.
Save bureyburey/2345dfa88a31e00a514479be37848d42 to your computer and use it in GitHub Desktop.
SplitAsyncStorage
/**
* custom wrapper for AsyncStorage that splits the data of a given storageKey to smaller chunks
* a large object with multiple keys will be spreaded to all the keys in the first level of the main object
* { data: { key1: value1, key2: value2, ...} }
* will be saved as key1 => value1, key2 => value2, ...
* this approach is intended to prevent the limitation of 2MB per value of AsyncStorage by spreading the values across the storage
*
* basic usage:
*
* import AsyncStorage from '@react-native-community/async-storage';
* const storage = SplitAsyncStorage(AsyncStorage);
*
* @param storageProvider - a storage provider instance such as AsyncStorage
* @param options - optional options object, passing "storageKey" will control which key should be splitted, defaults to "apollo-cache-persist"
* @returns {{removeItem: removeItem, getItem: getItem, setItem: setItem}}
* @constructor
*/
export default function SplitAsyncStorage(storageProvider, options = {}) {
if (!storageProvider) {
throw new Error('storageProvider is required!');
}
// the STORAGE_KEY fallback to the default key used by apollo-cache-persist
// if that key is changed manually, then this key should be changed as well by passing {storageKey: 'your-storage-key'} as options
const STORAGE_KEY = options.storageKey || 'apollo-cache-persist';
const STORAGE_KEY_KEYS = `${options.storageKey || STORAGE_KEY}-keys`;
return {
// spread all storageProvider methods
...storageProvider,
getItem: async (key) => {
// override getItem
let data;
if (key === STORAGE_KEY) {
// get all keys that needs to be extracted from the storage
const keys = await storageProvider.getItem(STORAGE_KEY_KEYS);
// get all the pieces of data
const splitData = await storageProvider.multiGet(JSON.parse(keys));
data = {};
// build the merged data object
for (let entry of splitData) {
if (typeof entry[1] === 'string') {
data[entry[0]] = JSON.parse(entry[1]);
} else {
data[entry[0]] = entry[1];
}
}
// return the data
return data;
}
// default behaviour
data = await storageProvider.getItem(key);
if (typeof data === 'string') {
return JSON.parse(data);
}
return data;
},
setItem: async (key, value) => {
// override setItem
let valueStr = value;
if (key === STORAGE_KEY) {
// parse the value if it isn't an object
let valueParsed = typeof value === 'object' ? value : JSON.parse(value);
// create an array of entries [[key1, value1], [key2, value2], ...]
const splitData = Object.entries(valueParsed);
// prepare the array of entries to be saved in the store
for (let entry of splitData) {
// stringify each value that is an object
if (typeof entry[1] === 'object') {
entry[1] = JSON.stringify(entry[1]);
}
}
// create an array of all the keys in the first level of the parsed value object and save it
const keys = Object.keys(valueParsed);
// store the values + the keys
return storageProvider.multiSet([...splitData, [STORAGE_KEY_KEYS, JSON.stringify(keys)]]);
}
// default behaviour
if (typeof valueStr === 'object') {
valueStr = JSON.stringify(value);
}
return storageProvider.setItem(key, valueStr);
},
removeItem: async (key) => {
// override removeItem
if (key === STORAGE_KEY) {
// get all keys that needs to be removed from the storage
const keys = await storageProvider.getItem(STORAGE_KEY_KEYS);
// remove all data + the keys
return storageProvider.multiRemove([...JSON.parse(keys), STORAGE_KEY_KEYS]);
}
// default behaviour
return storageProvider.removeItem(key);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment