UNPKG

react-query-external-sync

Version:

A tool for syncing React Query state to an external Dev Tools

174 lines 8.84 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { useEffect, useMemo, useState } from 'react'; import { useQueries } from '@tanstack/react-query'; import { storageQueryKeys } from './storageQueryKeys'; /** * Hook that creates individual React Query queries for each AsyncStorage key * This gives you granular control and better performance since each key has its own query * Since AsyncStorage doesn't have built-in change listeners, this hook uses polling to detect changes * * @example * // Get individual queries for all AsyncStorage keys * const queries = useDynamicAsyncStorageQueries({ queryClient }); * // Returns: [ * // { key: '@notifications:status', data: 'enabled', isLoading: false, error: null }, * // { key: '@user:preferences', data: { theme: 'dark' }, isLoading: false, error: null }, * // ... * // ] */ export function useDynamicAsyncStorageQueries({ queryClient, asyncStorage, pollInterval = 1000, enabled = true, }) { // State to track AsyncStorage keys (since getAllKeys is async) const [asyncStorageKeys, setAsyncStorageKeys] = useState([]); // Helper function to get a single AsyncStorage value const getAsyncStorageValue = useMemo(() => { return (key) => __awaiter(this, void 0, void 0, function* () { if (!asyncStorage) { return null; } try { const value = yield asyncStorage.getItem(key); if (value === null) { return null; } // Try to parse as JSON, fall back to string try { return JSON.parse(value); } catch (_a) { return value; } } catch (error) { console.error('Error getting AsyncStorage value for key:', key, error); throw error; } }); }, [asyncStorage]); // Function to refresh the list of AsyncStorage keys const refreshKeys = useMemo(() => { return () => __awaiter(this, void 0, void 0, function* () { if (!enabled || !asyncStorage) { setAsyncStorageKeys([]); return; } try { const keys = yield asyncStorage.getAllKeys(); // Filter out React Query cache and other noisy keys const filteredKeys = keys.filter((key) => !key.includes('REACT_QUERY_OFFLINE_CACHE') && !key.includes('RCTAsyncLocalStorage')); setAsyncStorageKeys([...filteredKeys]); // Convert readonly array to mutable array } catch (error) { console.error('📱 [AsyncStorage Hook] Error getting AsyncStorage keys:', error); setAsyncStorageKeys([]); } }); }, [enabled, asyncStorage]); // Initial load of keys useEffect(() => { refreshKeys(); }, [refreshKeys]); // Set up polling for key changes (since AsyncStorage doesn't have listeners) useEffect(() => { if (!enabled || pollInterval <= 0 || !asyncStorage) { return; } const interval = setInterval(() => __awaiter(this, void 0, void 0, function* () { try { const currentKeys = yield asyncStorage.getAllKeys(); // Filter out React Query cache and other noisy keys const filteredKeys = currentKeys.filter((key) => !key.includes('REACT_QUERY_OFFLINE_CACHE') && !key.includes('RCTAsyncLocalStorage')); // Check if keys have changed (added/removed) const keysChanged = filteredKeys.length !== asyncStorageKeys.length || !filteredKeys.every((key) => asyncStorageKeys.includes(key)); if (keysChanged) { console.log('🔄 [AsyncStorage Hook] AsyncStorage keys changed!'); console.log('🔄 [AsyncStorage Hook] Old keys:', asyncStorageKeys.length); console.log('🔄 [AsyncStorage Hook] New keys:', filteredKeys.length); setAsyncStorageKeys([...filteredKeys]); // Convert readonly array to mutable array // Invalidate all AsyncStorage queries to refresh data queryClient.invalidateQueries({ queryKey: storageQueryKeys.async.root(), }); } else { // Keys are the same, but check if any values have changed for (const key of asyncStorageKeys) { try { // Check if the query exists in the cache first const queryExists = queryClient.getQueryCache().find({ queryKey: storageQueryKeys.async.key(key) }); // If query doesn't exist (e.g., after cache clear), skip comparison // The useQueries hook will recreate the query automatically if (!queryExists) { continue; } // Get current value from AsyncStorage const currentValue = yield getAsyncStorageValue(key); // Get cached value from React Query const cachedData = queryClient.getQueryData(storageQueryKeys.async.key(key)); // Only compare if we have cached data (avoid false positives after cache clear) if (cachedData !== undefined) { // Compare values (deep comparison for objects) const valuesAreDifferent = JSON.stringify(currentValue) !== JSON.stringify(cachedData); if (valuesAreDifferent) { console.log('🔄 [AsyncStorage Hook] Value changed for key:', key); // Invalidate this specific query queryClient.invalidateQueries({ queryKey: storageQueryKeys.async.key(key), }); } } } catch (error) { console.error('📱 [AsyncStorage Hook] Error checking value for key:', key, error); } } } } catch (error) { console.error('📱 [AsyncStorage Hook] Error polling AsyncStorage keys:', error); } }), pollInterval); return () => { clearInterval(interval); }; }, [pollInterval, asyncStorageKeys, queryClient, getAsyncStorageValue, enabled, asyncStorage]); // Create individual queries for each key const queries = useQueries({ queries: enabled && asyncStorage ? asyncStorageKeys.map((key) => ({ queryKey: storageQueryKeys.async.key(key), queryFn: () => __awaiter(this, void 0, void 0, function* () { const value = yield getAsyncStorageValue(key); return value; }), staleTime: pollInterval > 0 ? pollInterval / 2 : 0, // Half the poll interval gcTime: 5 * 60 * 1000, // 5 minutes networkMode: 'always', retry: 0, // Retry failed requests retryDelay: 100, // 1 second delay between retries })) : [], combine: (results) => { if (!enabled || !asyncStorage) { return []; } const combinedResults = results.map((result, index) => ({ key: asyncStorageKeys[index], data: result.data, isLoading: result.isLoading, error: result.error, })); return combinedResults; }, }, queryClient); return queries; } //# sourceMappingURL=useDynamicAsyncStorageQueries.js.map