UNPKG

react-native-onyx

Version:

State management for React Native

131 lines (130 loc) 5.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OnyxSnapshotCache = void 0; const OnyxUtils_1 = __importDefault(require("./OnyxUtils")); /** * Manages snapshot caching for useOnyx hook performance optimization. * Handles selector function tracking and memoized getSnapshot results. */ class OnyxSnapshotCache { constructor() { this.snapshotCache = new Map(); this.selectorIDMap = new Map(); this.selectorIDCounter = 0; this.cacheKeyRefCounts = new Map(); } /** * Generate unique ID for selector functions using incrementing numbers */ getSelectorID(selector) { const typedSelector = selector; if (!this.selectorIDMap.has(typedSelector)) { const id = this.selectorIDCounter++; this.selectorIDMap.set(typedSelector, id); } return this.selectorIDMap.get(typedSelector); } /** * Register a consumer for a cache key and return the cache key. * Generates cache key and increments reference counter. * * The properties used to generate the cache key are handpicked for performance reasons and * according to their purpose and effect they produce in the useOnyx hook behavior: * * - `selector`: Different selectors produce different results, so each selector needs its own cache entry * - `initWithStoredValues`: This flag changes the initial loading behavior and affects the returned fetch status * - `allowStaleData`: Controls whether stale data can be returned during pending merges, affecting result timing * - `canBeMissing`: Determines logging behavior for missing data, but doesn't affect the actual data returned * * Other options like `canEvict`, `reuseConnection`, and `allowDynamicKey` don't affect the data transformation * or timing behavior of getSnapshot, so they're excluded from the cache key for better cache hit rates. */ registerConsumer(options) { var _a, _b, _c; const selectorID = (options === null || options === void 0 ? void 0 : options.selector) ? this.getSelectorID(options.selector) : 'no_selector'; // Create options hash without expensive JSON.stringify const initWithStoredValues = (_a = options === null || options === void 0 ? void 0 : options.initWithStoredValues) !== null && _a !== void 0 ? _a : true; const allowStaleData = (_b = options === null || options === void 0 ? void 0 : options.allowStaleData) !== null && _b !== void 0 ? _b : false; const canBeMissing = (_c = options === null || options === void 0 ? void 0 : options.canBeMissing) !== null && _c !== void 0 ? _c : true; const cacheKey = `${selectorID}_${initWithStoredValues}_${allowStaleData}_${canBeMissing}`; // Increment reference count for this cache key const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0; this.cacheKeyRefCounts.set(cacheKey, currentCount + 1); return cacheKey; } /** * Deregister a consumer for a cache key. * Decrements reference counter and removes cache entry if no consumers remain. */ deregisterConsumer(key, cacheKey) { const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0; if (currentCount <= 1) { // Last consumer - remove from reference counter and cache this.cacheKeyRefCounts.delete(cacheKey); // Remove from snapshot cache const keyCache = this.snapshotCache.get(key); if (keyCache) { keyCache.delete(cacheKey); // If this was the last cache entry for this Onyx key, remove the key entirely if (keyCache.size === 0) { this.snapshotCache.delete(key); } } } else { // Still has other consumers - just decrement count this.cacheKeyRefCounts.set(cacheKey, currentCount - 1); } } /** * Get cached snapshot result for a key and cache key combination */ getCachedResult(key, cacheKey) { const keyCache = this.snapshotCache.get(key); return keyCache === null || keyCache === void 0 ? void 0 : keyCache.get(cacheKey); } /** * Set cached snapshot result for a key and cache key combination */ setCachedResult(key, cacheKey, result) { if (!this.snapshotCache.has(key)) { this.snapshotCache.set(key, new Map()); } this.snapshotCache.get(key).set(cacheKey, result); } /** * Selective cache invalidation to prevent data unavailability * Collection members invalidate upward, collections don't cascade downward */ invalidateForKey(keyToInvalidate) { // Always invalidate the exact key this.snapshotCache.delete(keyToInvalidate); try { // Check if the key is a collection member and invalidate the collection base key const collectionBaseKey = OnyxUtils_1.default.getCollectionKey(keyToInvalidate); this.snapshotCache.delete(collectionBaseKey); } catch (e) { // do nothing - this just means the key is not a collection member } } /** * Clear all snapshot cache */ clear() { this.snapshotCache.clear(); } /** * Clear selector ID mappings (useful for testing) */ clearSelectorIds() { this.selectorIDCounter = 0; } } exports.OnyxSnapshotCache = OnyxSnapshotCache; // Create and export a singleton instance const onyxSnapshotCache = new OnyxSnapshotCache(); exports.default = onyxSnapshotCache;