CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-firebase--firestore

React Native Firebase Cloud Firestore provides a NoSQL document database with real-time synchronization capabilities.

Pending
Overview
Eval results
Files

offline-network.mddocs/

Offline & Network Management

Comprehensive offline support with configurable persistence, network management, and cache control.

Capabilities

Network Control

Manage network connectivity to control when Firestore communicates with the server.

/**
 * Enable network connectivity for Firestore
 * @returns Promise resolving when network is enabled
 */
enableNetwork(): Promise<void>;

/**
 * Disable network connectivity for Firestore
 * @returns Promise resolving when network is disabled
 */
disableNetwork(): Promise<void>;

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Enable network (default state)
await firestore().enableNetwork();
console.log('Network enabled - can sync with server');

// Disable network for offline-only operation
await firestore().disableNetwork();
console.log('Network disabled - operating in offline mode');

// Re-enable network
await firestore().enableNetwork();
console.log('Network re-enabled - will sync pending changes');

// Network state management based on app state
import { AppState } from 'react-native';

class NetworkManager {
  constructor() {
    AppState.addEventListener('change', this.handleAppStateChange);
  }

  handleAppStateChange = async (nextAppState: string) => {
    const db = firestore();
    
    if (nextAppState === 'background') {
      // Disable network to save battery/data when app is backgrounded
      await db.disableNetwork();
      console.log('App backgrounded - network disabled');
    } else if (nextAppState === 'active') {
      // Re-enable network when app becomes active
      await db.enableNetwork();
      console.log('App active - network enabled');
    }
  };
}

Persistence Management

Control offline data persistence and cache behavior.

/**
 * Clear all cached data and pending writes
 * @returns Promise resolving when persistence is cleared
 */
clearPersistence(): Promise<void>;

/**
 * Wait for all pending writes to complete
 * @returns Promise resolving when all writes are committed
 */
waitForPendingWrites(): Promise<void>;

/**
 * Terminate the Firestore instance and clean up resources
 * @returns Promise resolving when termination is complete
 */
terminate(): Promise<void>;

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Clear all cached data (useful for user logout)
async function clearUserData() {
  const db = firestore();
  
  // First disable network to prevent new data
  await db.disableNetwork();
  
  // Clear all cached data
  await db.clearPersistence();
  
  console.log('All cached data cleared');
  
  // Re-enable network for fresh start
  await db.enableNetwork();
}

// Wait for pending writes before performing sensitive operations
async function performSecureOperation() {
  const db = firestore();
  
  // Ensure all pending writes are committed
  await db.waitForPendingWrites();
  console.log('All writes committed to server');
  
  // Now safe to perform secure operation
  await performSensitiveTask();
}

// Clean shutdown of Firestore
async function shutdownFirestore() {
  const db = firestore();
  
  // Wait for pending operations
  await db.waitForPendingWrites();
  
  // Terminate the instance
  await db.terminate();
  
  console.log('Firestore instance terminated');
}

// Handle app lifecycle for proper cleanup
class FirestoreManager {
  async componentWillUnmount() {
    try {
      // Ensure data is persisted before app closes
      await firestore().waitForPendingWrites();
    } catch (error) {
      console.error('Error waiting for writes:', error);
    }
  }
}

Settings Configuration

Configure Firestore behavior including cache size, persistence, and server settings.

/**
 * Configure Firestore settings
 * @param settings - Configuration options
 * @returns Promise resolving when settings are applied
 */
settings(settings: FirebaseFirestoreTypes.Settings): Promise<void>;

interface Settings {
  /**
   * Enable or disable offline persistence
   * @default true
   */
  persistence?: boolean;

  /**
   * Cache size in bytes (-1 for unlimited)
   * @default 104857600 (100 MB)
   */
  cacheSizeBytes?: number;

  /**
   * Firestore host (for custom endpoints)
   */
  host?: string;

  /**
   * Enable or disable SSL
   * @default true
   */
  ssl?: boolean;

  /**
   * Ignore undefined properties in document data
   * @default false
   */
  ignoreUndefinedProperties?: boolean;

  /**
   * Behavior for server timestamps that haven't been set yet
   * @default 'none'
   */
  serverTimestampBehavior?: 'estimate' | 'previous' | 'none';
}

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Configure basic settings
await firestore().settings({
  persistence: true,
  cacheSizeBytes: 50 * 1024 * 1024, // 50 MB cache
  ignoreUndefinedProperties: true
});

// Unlimited cache size
await firestore().settings({
  cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED
});

// Custom server timestamp behavior
await firestore().settings({
  serverTimestampBehavior: 'estimate' // Use local time estimate
});

// Development settings with custom host
await firestore().settings({
  host: 'localhost:8080',
  ssl: false,
  persistence: false // Disable for development
});

// Production optimized settings
await firestore().settings({
  persistence: true,
  cacheSizeBytes: 100 * 1024 * 1024, // 100 MB
  ignoreUndefinedProperties: true,
  serverTimestampBehavior: 'estimate'
});

// Memory-constrained device settings
await firestore().settings({
  persistence: true,
  cacheSizeBytes: 10 * 1024 * 1024, // 10 MB cache
  ignoreUndefinedProperties: true
});

Emulator Support

Connect to the Firestore emulator for development and testing.

/**
 * Connect to Firestore emulator
 * @param host - Emulator host
 * @param port - Emulator port
 * @returns Array containing actual host and port used (for testing)
 */
useEmulator(host: string, port: number): [string, number];

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Connect to local emulator
if (__DEV__) {
  // Connect to emulator running on localhost:8080
  firestore().useEmulator('localhost', 8080);
  console.log('Connected to Firestore emulator');
}

// Connect to emulator with custom configuration
if (__DEV__) {
  const db = firestore();
  
  // Use emulator
  db.useEmulator('127.0.0.1', 8080);
  
  // Configure for development
  await db.settings({
    persistence: false, // Disable persistence in development
    cacheSizeBytes: 1024 * 1024 // 1 MB cache for testing
  });
}

// Environment-based emulator configuration
const initializeFirestore = async () => {
  const db = firestore();
  
  if (process.env.NODE_ENV === 'development') {
    // Development: use emulator
    db.useEmulator('localhost', 8080);
    
    await db.settings({
      persistence: false,
      ignoreUndefinedProperties: true
    });
  } else if (process.env.NODE_ENV === 'test') {
    // Testing: use emulator with clean state
    db.useEmulator('localhost', 8080);
    
    await db.clearPersistence();
    await db.settings({ persistence: false });
  } else {
    // Production: use real Firestore
    await db.settings({
      persistence: true,
      cacheSizeBytes: 50 * 1024 * 1024,
      ignoreUndefinedProperties: true
    });
  }
};

Bundle Operations

Load pre-packaged data bundles for efficient initial data loading and execute pre-configured queries.

/**
 * Load a data bundle containing documents and queries
 * @param bundle - Bundle data as string
 * @returns Promise resolving to load bundle task progress
 */
loadBundle(bundle: string): Promise<FirebaseFirestoreTypes.LoadBundleTaskProgress>;

/**
 * Get a pre-configured query from a loaded bundle
 * @param queryName - Name of the query in the bundle
 * @returns Query instance or null if not found
 */
namedQuery(queryName: string): FirebaseFirestoreTypes.Query | null;

interface LoadBundleTaskProgress {
  /**
   * Number of documents loaded so far
   */
  documentsLoaded: number;
  
  /**
   * Total number of documents in the bundle
   */
  totalDocuments: number;
  
  /**
   * Number of bytes loaded so far
   */
  bytesLoaded: number;
  
  /**
   * Total number of bytes in the bundle
   */
  totalBytes: number;
  
  /**
   * Current task state
   */
  taskState: FirebaseFirestoreTypes.TaskState;
}

type TaskState = 'Error' | 'Running' | 'Success';

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Load a data bundle
async function loadInitialData() {
  const db = firestore();
  
  try {
    // Bundle data (typically fetched from server or included in app)
    const bundleData = await fetch('/api/firestore-bundle').then(r => r.text());
    
    // Load the bundle
    const progress = await db.loadBundle(bundleData);
    
    console.log('Bundle loaded successfully:');
    console.log(`Documents: ${progress.documentsLoaded}/${progress.totalDocuments}`);
    console.log(`Bytes: ${progress.bytesLoaded}/${progress.totalBytes}`);
    console.log(`Status: ${progress.taskState}`);
    
    // Use named queries from the bundle
    const featuredProductsQuery = db.namedQuery('featured-products');
    if (featuredProductsQuery) {
      const snapshot = await featuredProductsQuery.get();
      console.log(`Found ${snapshot.size} featured products`);
    }
    
  } catch (error) {
    console.error('Error loading bundle:', error);
  }
}

// Progressive bundle loading with progress tracking
async function loadBundleWithProgress(bundleData: string) {
  const db = firestore();
  
  // Note: In practice, you might want to implement progress tracking
  // This is a simplified example showing the final result
  const progress = await db.loadBundle(bundleData);
  
  if (progress.taskState === 'Success') {
    console.log('Bundle loaded successfully');
    
    // Access bundled queries
    const popularQuery = db.namedQuery('popular-items');
    const recentQuery = db.namedQuery('recent-activity');
    
    if (popularQuery && recentQuery) {
      const [popularResults, recentResults] = await Promise.all([
        popularQuery.get(),
        recentQuery.get()
      ]);
      
      console.log('Popular items:', popularResults.size);
      console.log('Recent activity:', recentResults.size);
    }
  } else if (progress.taskState === 'Error') {
    console.error('Bundle loading failed');
  }
}

// Offline-first app initialization with bundles
async function initializeOfflineApp() {
  const db = firestore();
  
  // Load cached bundle for immediate offline access
  try {
    const cachedBundle = await getCachedBundle(); // Your caching logic
    if (cachedBundle) {
      await db.loadBundle(cachedBundle);
      console.log('Offline bundle loaded');
    }
  } catch (error) {
    console.log('No cached bundle available');
  }
  
  // Try to load fresh bundle when online
  try {
    const freshBundle = await fetchFreshBundle(); // Your fetch logic
    if (freshBundle) {
      await db.loadBundle(freshBundle);
      console.log('Fresh bundle loaded');
      await cacheBundleForOffline(freshBundle);
    }
  } catch (error) {
    console.log('Could not load fresh bundle, using cached version');
  }
}

Cache Management

Access and manage persistent cache index operations.

/**
 * Get persistent cache index manager (null if persistence disabled)
 * @returns PersistentCacheIndexManager instance or null
 */
persistentCacheIndexManager(): FirebaseFirestoreTypes.PersistentCacheIndexManager | null;

interface PersistentCacheIndexManager {
  /**
   * Enable automatic index creation for queries
   * @returns Promise resolving when auto-creation is enabled
   */
  enablePersistentCacheIndexAutoCreation(): Promise<void>;

  /**
   * Disable automatic index creation for queries
   * @returns Promise resolving when auto-creation is disabled
   */
  disablePersistentCacheIndexAutoCreation(): Promise<void>;

  /**
   * Delete all persistent cache indexes
   * @returns Promise resolving when all indexes are deleted
   */
  deleteAllPersistentCacheIndexes(): Promise<void>;
}

Usage Examples:

import firestore from '@react-native-firebase/firestore';

// Manage cache indexes
async function manageCacheIndexes() {
  const db = firestore();
  const indexManager = db.persistentCacheIndexManager();
  
  if (indexManager) {
    // Enable automatic index creation
    await indexManager.enablePersistentCacheIndexAutoCreation();
    console.log('Auto index creation enabled');
    
    // Later, if you need to clean up indexes
    await indexManager.deleteAllPersistentCacheIndexes();
    console.log('All cache indexes deleted');
    
    // Disable auto creation
    await indexManager.disablePersistentCacheIndexAutoCreation();
    console.log('Auto index creation disabled');
  } else {
    console.log('Cache persistence is disabled - no index manager available');
  }
}

// Initialize with index management
async function initializeWithCacheManagement() {
  const db = firestore();
  
  // Configure persistence
  await db.settings({
    persistence: true,
    cacheSizeBytes: 100 * 1024 * 1024
  });
  
  // Manage indexes
  const indexManager = db.persistentCacheIndexManager();
  if (indexManager) {
    await indexManager.enablePersistentCacheIndexAutoCreation();
  }
}

Offline Data Patterns

Best practices for working with offline data and handling connectivity changes.

Usage Examples:

import firestore from '@react-native-firebase/firestore';
import NetInfo from '@react-native-netinfo/netinfo';

class OfflineManager {
  private isOnline = true;
  private pendingWrites: Array<() => Promise<void>> = [];
  
  constructor() {
    this.setupNetworkListener();
    this.setupFirestoreSettings();
  }
  
  private async setupFirestoreSettings() {
    await firestore().settings({
      persistence: true,
      cacheSizeBytes: 50 * 1024 * 1024,
      ignoreUndefinedProperties: true,
      serverTimestampBehavior: 'estimate'
    });
  }
  
  private setupNetworkListener() {
    NetInfo.addEventListener(state => {
      const wasOnline = this.isOnline;
      this.isOnline = state.isConnected ?? false;
      
      if (!wasOnline && this.isOnline) {
        this.handleConnectionRestored();
      } else if (wasOnline && !this.isOnline) {
        this.handleConnectionLost();
      }
    });
  }
  
  private async handleConnectionRestored() {
    console.log('Connection restored - syncing data');
    
    try {
      // Enable network
      await firestore().enableNetwork();
      
      // Wait for pending writes to sync
      await firestore().waitForPendingWrites();
      
      console.log('All data synced successfully');
    } catch (error) {
      console.error('Error syncing data:', error);
    }
  }
  
  private async handleConnectionLost() {
    console.log('Connection lost - operating offline');
    // Firestore automatically handles offline mode
    // No need to explicitly disable network
  }
  
  // Queue writes for when connection is restored
  async queueWrite(writeOperation: () => Promise<void>) {
    if (this.isOnline) {
      await writeOperation();
    } else {
      this.pendingWrites.push(writeOperation);
      console.log('Write queued for when connection is restored');
    }
  }
  
  // Get data with source preference
  async getData(docRef: firestore.DocumentReference, preferCache = false) {
    try {
      if (preferCache || !this.isOnline) {
        // Try cache first
        const cacheSnapshot = await docRef.get({ source: 'cache' });
        if (cacheSnapshot.exists) {
          return cacheSnapshot;
        }
      }
      
      // Fall back to server
      return await docRef.get({ source: 'server' });
    } catch (error) {
      // If server fails, try cache
      return await docRef.get({ source: 'cache' });
    }
  }
}

// Usage with real-time listeners that handle offline state
const setupOfflineAwareListener = (docRef: firestore.DocumentReference) => {
  return docRef.onSnapshot(
    { includeMetadataChanges: true },
    snapshot => {
      if (snapshot.exists) {
        const data = snapshot.data();
        const isFromCache = snapshot.metadata.fromCache;
        const hasPendingWrites = snapshot.metadata.hasPendingWrites;
        
        console.log('Data:', data);
        console.log('From cache:', isFromCache);
        console.log('Has pending writes:', hasPendingWrites);
        
        // Update UI with offline indicators
        updateUI(data, { offline: isFromCache, pending: hasPendingWrites });
      }
    },
    error => {
      console.error('Listener error:', error);
      // Handle offline errors gracefully
      if (error.code === 'unavailable') {
        console.log('Service unavailable - using cached data');
      }
    }
  );
};

Types

/**
 * Cache size constant for unlimited cache
 */
const CACHE_SIZE_UNLIMITED: -1;

/**
 * Settings interface for Firestore configuration
 */
interface Settings {
  persistence?: boolean;
  cacheSizeBytes?: number;
  host?: string;
  ssl?: boolean;
  ignoreUndefinedProperties?: boolean;
  serverTimestampBehavior?: 'estimate' | 'previous' | 'none';
}

/**
 * Get options for specifying data source
 */
interface GetOptions {
  source: 'default' | 'server' | 'cache';
}

/**
 * Persistent cache index manager for query optimization
 */
interface PersistentCacheIndexManager {
  enablePersistentCacheIndexAutoCreation(): Promise<void>;
  disablePersistentCacheIndexAutoCreation(): Promise<void>;
  deleteAllPersistentCacheIndexes(): Promise<void>;
}

Install with Tessl CLI

npx tessl i tessl/npm-react-native-firebase--firestore

docs

data-types.md

database-operations.md

index.md

modular-api.md

offline-network.md

querying-filtering.md

realtime-sync.md

transactions-batches.md

tile.json