npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

akshay-khapare-react-native-firebase-hooks

v1.0.3

Published

Production-ready React Native Firebase hooks for efficient Firestore operations with comprehensive error handling, TypeScript support, and optimized performance

Readme

🔥 Akshay Khapare React Native Firebase Hooks

npm version License: MIT TypeScript Production Ready Error Handling

The most comprehensive, production-ready React Native Firebase hooks library with advanced error handling, zero crashes, and complete type safety.

✨ Key Features

  • 🛡️ Zero App Crashes - Comprehensive error handling with 55+ specific error codes
  • 🔄 Real-time Data - Advanced listeners with metadata and cache control
  • Performance Optimized - Efficient queries, pagination, and batch operations
  • 🎯 Type Safe - Full TypeScript support with generics
  • 🏗️ Production Ready - Enterprise-grade error reporting and debugging
  • 🔧 Multi-Project - Seamless multi-Firebase project support
  • 📊 Complete Coverage - 105 tests ensuring reliability

📦 Installation

npm install akshay-khapare-react-native-firebase-hooks

Required Peer Dependencies

npm install @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/auth @react-native-firebase/storage

🚀 Quick Start

📱 React Native Components: All code examples use React Native components (View, Text, TouchableOpacity, FlatList, TextInput, etc.). Import these from 'react-native' in your components. For data persistence, examples use AsyncStorage from @react-native-async-storage/async-storage.

import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import {
  initializeFirebase,
  useFirestoreSet,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

// Initialize Firebase
await initializeFirebase({
  projectName: '[DEFAULT]',
  config: firebaseConfig,
  onError: (error) => console.error('Init failed:', error.code),
});

// Use hooks with error handling - No try/catch needed!
const { setData } = useFirestoreSet();

const createUser = async (userData) => {
  const docId = await setData({
    collection: 'users',
    doc: 'user123',
    data: userData,
    onError: (error) => {
      // Handle specific errors with error codes
      switch (error.code) {
        case ERROR_CODES.VALIDATION_DATA_EMPTY:
          showToast('Please fill in user data');
          break;
        case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
          showToast('Permission denied. Please login.');
          break;
        default:
          showToast('Failed to create user');
      }
    },
  });

  if (docId) {
    showToast('User created successfully!');
  }
};

📖 Complete API Documentation

Single Project Setup

import {
  initializeFirebase,
  configureFirestore,
} from 'akshay-khapare-react-native-firebase-hooks';

const setupFirebase = async () => {
  await initializeFirebase({
    projectName: '[DEFAULT]',
    config: {
      apiKey: 'your-api-key',
      authDomain: 'your-project.firebaseapp.com',
      projectId: 'your-project-id',
      storageBucket: 'your-project.appspot.com',
      messagingSenderId: '123456789',
      appId: '1:123456789:web:abcdef',
    },
    onError: (error) => {
      logError('Firebase init failed', error);
    },
  });

  await configureFirestore({
    projectName: '[DEFAULT]',
    persistence: true,
    cacheSizeBytes: -1, // Unlimited cache
  });
};

Multi-Project Setup

import { initializeMultipleFirebaseProjects } from 'akshay-khapare-react-native-firebase-hooks';

await initializeMultipleFirebaseProjects({
  configs: [
    { name: 'production', config: prodConfig },
    { name: 'analytics', config: analyticsConfig },
  ],
  onError: (error) => console.error('Multi-project setup failed:', error),
});

Firebase Service Exports

The library also exports Firebase service instances for direct access:

import {
  auth,
  firestore,
  storage,
} from 'akshay-khapare-react-native-firebase-hooks';

// Direct access to Firebase services
const AuthService = () => {
  const signInUser = async () => {
    try {
      await auth().signInWithEmailAndPassword(email, password);
    } catch (error) {
      console.error('Auth error:', error);
    }
  };
};

const DirectFirestoreAccess = () => {
  const directOperation = async () => {
    // Direct Firestore access for complex operations
    const batch = firestore().batch();
    const userRef = firestore().collection('users').doc('user123');

    batch.set(userRef, { name: 'John' });
    await batch.commit();
  };
};

const StorageOperations = () => {
  const uploadFile = async (file) => {
    const ref = storage().ref(`uploads/${file.name}`);
    await ref.putFile(file);
    return ref.getDownloadURL();
  };
};

Basic Usage

import {
  useFirestoreSet,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const CreateUser = () => {
  const { setData } = useFirestoreSet();

  const createUser = async () => {
    const docId = await setData({
      collection: 'users',
      doc: 'user123', // or use firestore().collection('users').doc().id for auto-ID
      data: {
        name: 'John Doe',
        email: '[email protected]',
        status: 'active',
      },
      merge: false, // true to merge with existing data
      addTimestamp: true, // Adds updatedAt server timestamp
      onError: (error) => handleSetError(error),
    });

    if (docId) {
      console.log('User created with ID:', docId);
    }
  };

  const handleSetError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_COLLECTION_EMPTY:
        showToast('Collection name is required');
        break;
      case ERROR_CODES.VALIDATION_DOCUMENT_EMPTY:
        showToast('Document ID is required');
        break;
      case ERROR_CODES.VALIDATION_DATA_INVALID:
        showToast('Invalid data format');
        break;
      case ERROR_CODES.VALIDATION_DATA_EMPTY:
        showToast('Data cannot be empty');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Permission denied');
        break;
      case ERROR_CODES.OPERATION_SET_FAILED:
        showToast('Failed to save data');
        break;
      default:
        showToast('An error occurred');
    }
  };
};

Advanced Usage with Multi-Project

const { setData } = useFirestoreSet();

// Create user in specific project
await setData({
  collection: 'users',
  doc: userId,
  data: userData,
  firebaseProject: 'analytics', // Use specific project
  merge: true,
  addTimestamp: true,
  onError: (error) => {
    logToAnalytics('user_creation_failed', {
      errorCode: error.code,
      errorNumber: error.codeNumber,
      category: error.category,
    });
  },
});

Parameters

| Parameter | Type | Required | Description | | ----------------- | ------------------------------------ | -------- | ----------------------------------------- | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | data | Record<string, unknown> | ✅ | Document data | | merge | boolean | ❌ | Merge with existing data (default: false) | | addTimestamp | boolean | ❌ | Add updatedAt timestamp (default: false) | | firebaseProject | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

Basic Document Fetching

import { useFirestoreGet, ERROR_CODES } from 'akshay-khapare-react-native-firebase-hooks';

const UserProfile = ({ userId }) => {
  const { getData } = useFirestoreGet();
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);

  const fetchUser = async (source = 'default') => {
    setLoading(true);

    const result = await getData({
      collection: 'users',
      doc: userId,
      source, // 'default' | 'server' | 'cache'
      onError: (error) => handleGetError(error)
    });

    setLoading(false);

    if (result) {
      setUser({
        id: result.id,
        ...result.data,
        isFromCache: result.fromCache,
        hasPendingWrites: result.hasPendingWrites
      });
    }
  };

  const handleGetError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_COLLECTION_EMPTY:
      case ERROR_CODES.VALIDATION_DOCUMENT_EMPTY:
        showToast('Invalid document reference');
        break;
      case ERROR_CODES.VALIDATION_SOURCE_INVALID:
        showToast('Invalid data source specified');
        break;
      case ERROR_CODES.FIREBASE_DOCUMENT_NOT_FOUND:
        showToast('User not found');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Access denied');
        break;
      case ERROR_CODES.OPERATION_GET_FAILED:
        showToast('Failed to load user data');
        break;
      default:
        showToast('An error occurred while loading');
    }
  };

  return (
    <View>
      <TouchableOpacity onPress={() => fetchUser('cache')}>
        <Text>Load from Cache</Text>
      </TouchableOpacity>
      <TouchableOpacity onPress={() => fetchUser('server')}>
        <Text>Refresh from Server</Text>
      </TouchableOpacity>
      {loading && <LoadingSpinner />}
      {user && <UserDisplay user={user} />}
    </View>
  );
};

Parameters

| Parameter | Type | Required | Description | | --------------------- | ------------------------------------ | -------- | -------------------------------- | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | source | 'default' \| 'server' \| 'cache' | ❌ | Data source (default: 'default') | | firebaseProjectName | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

Response Format

interface DocumentResponse<T> {
  id: string; // Document ID
  exists: boolean; // Whether document exists
  data: T | null; // Document data
  fromCache: boolean; // Data from cache
  hasPendingWrites: boolean; // Has pending writes
}

Document Updates

import {
  useFirestoreUpdate,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const UpdateUserProfile = () => {
  const { updateData } = useFirestoreUpdate();

  const updateUserStatus = async (userId, status) => {
    const result = await updateData({
      collection: 'users',
      doc: userId,
      data: {
        status,
        lastModified: new Date(),
      },
      addTimestamp: true, // Adds updatedAt server timestamp
      onError: (error) => handleUpdateError(error, userId),
    });

    if (result) {
      showToast('User status updated successfully');
    }
  };

  const handleUpdateError = (error, userId) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_DATA_EMPTY:
        showToast('No data provided for update');
        break;
      case ERROR_CODES.FIREBASE_DOCUMENT_NOT_FOUND:
        showToast('User not found');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Permission denied to update user');
        break;
      case ERROR_CODES.OPERATION_UPDATE_FAILED:
        showToast('Failed to update user');
        logError('User update failed', { userId, error });
        break;
      default:
        showToast('Update failed');
    }
  };
};

Parameters

| Parameter | Type | Required | Description | | ----------------- | ------------------------------------ | -------- | ----------------------- | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | data | Partial<T> | ✅ | Update data | | addTimestamp | boolean | ❌ | Add updatedAt timestamp | | firebaseProject | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

Complex Queries with Pagination

import {
  useFirestoreGetQuery,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const ProductsList = () => {
  const { getQuery } = useFirestoreGetQuery();
  const [products, setProducts] = useState([]);
  const [lastDoc, setLastDoc] = useState(null);
  const [loading, setLoading] = useState(false);

  const loadProducts = async (loadMore = false) => {
    setLoading(true);

    const result = await getQuery(
      {
        collection: 'products',
        where: [
          ['status', '==', 'published'],
          ['price', '>', 0],
          ['category', 'in', ['electronics', 'books']],
        ],
        orderBy: [
          ['featured', 'desc'],
          ['createdAt', 'desc'],
        ],
        limit: 20,
        ...(loadMore && lastDoc ? { startAfter: lastDoc } : {}),
        onError: (error) => handleQueryError(error),
      },
      {
        source: 'default', // Query options
      }
    );

    setLoading(false);

    if (result) {
      const newProducts = result.map((doc) => ({
        id: doc.id,
        ...doc.data,
        isFromCache: doc.fromCache,
      }));

      setProducts((prev) =>
        loadMore ? [...prev, ...newProducts] : newProducts
      );
      if (result.length > 0) {
        setLastDoc(result[result.length - 1]);
      }
    }
  };

  const handleQueryError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_WHERE_CLAUSE_INVALID:
        showToast('Invalid search criteria');
        break;
      case ERROR_CODES.VALIDATION_ORDER_BY_INVALID:
        showToast('Invalid sorting criteria');
        break;
      case ERROR_CODES.VALIDATION_LIMIT_INVALID:
        showToast('Invalid page size');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Access denied to products');
        break;
      case ERROR_CODES.OPERATION_QUERY_FAILED:
        showToast('Failed to load products');
        break;
      default:
        showToast('Search failed');
    }
  };
};

Parameters

| Parameter | Type | Required | Description | | --------------------- | ------------------------------------ | -------- | -------------------------------- | | collection | string | ✅ | Collection name | | where | [string, WhereFilterOp, unknown][] | ❌ | Where conditions | | orderBy | [string, 'asc' \| 'desc'][] | ❌ | Sort conditions | | limit | number | ❌ | Results limit (positive integer) | | startAt | unknown | ❌ | Start cursor value | | startAfter | unknown | ❌ | Start after cursor value | | endAt | unknown | ❌ | End cursor value | | endBefore | unknown | ❌ | End before cursor value | | firebaseProjectName | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

Query Options

| Parameter | Type | Required | Description | | --------- | ---------------------------------- | -------- | ----------------- | | source | 'default' \| 'server' \| 'cache' | ❌ | Query data source |

Response Format

interface QueryResult<T> {
  id: string; // Document ID
  data: T; // Document data
  fromCache: boolean; // Data retrieved from cache
  hasPendingWrites: boolean; // Document has pending writes
}

Advanced Usage with Cursors

const PaginatedQuery = () => {
  const { getQuery } = useFirestoreGetQuery();
  const [lastVisible, setLastVisible] = useState(null);
  const [firstVisible, setFirstVisible] = useState(null);

  // Forward pagination
  const loadNext = async () => {
    const result = await getQuery({
      collection: 'posts',
      orderBy: [['createdAt', 'desc']],
      limit: 10,
      startAfter: lastVisible, // Start after last document
      onError: (error) => {
        switch (error.code) {
          case ERROR_CODES.VALIDATION_LIMIT_INVALID:
            showToast('Invalid page size specified');
            break;
          default:
            showToast('Failed to load next page');
        }
      },
    });

    if (result && result.length > 0) {
      setLastVisible(result[result.length - 1]);
      if (!firstVisible) setFirstVisible(result[0]);
    }
  };

  // Backward pagination
  const loadPrevious = async () => {
    const result = await getQuery({
      collection: 'posts',
      orderBy: [['createdAt', 'desc']],
      limit: 10,
      endBefore: firstVisible, // End before first document
      onError: (error) => handleQueryError(error),
    });

    if (result && result.length > 0) {
      setFirstVisible(result[0]);
      setLastVisible(result[result.length - 1]);
    }
  };
};

Real-time Collection Monitoring

import {
  useCollectionListener,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const ChatMessages = ({ chatId }) => {
  const { listenToCollection } = useCollectionListener();
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const unsubscribe = listenToCollection({
      collection: 'messages',
      where: [['chatId', '==', chatId]],
      orderBy: [['timestamp', 'desc']],
      limit: 50,
      includeMetadataChanges: true,

      onData: (messageData) => {
        const newMessages = messageData.map((doc) => ({
          id: doc.id,
          ...doc.data,
          isPending: doc.hasPendingWrites,
          isFromCache: doc.fromCache,
        }));
        setMessages(newMessages);
      },

      onError: (error) => handleListenerError(error),
    });

    return () => unsubscribe();
  }, [chatId]);

  const handleListenerError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_CALLBACK_MISSING:
        console.error('onData callback is required');
        break;
      case ERROR_CODES.LISTENER_SETUP_FAILED:
        showToast('Failed to connect to chat');
        break;
      case ERROR_CODES.LISTENER_SNAPSHOT_ERROR:
        showToast('Connection interrupted');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Access denied to chat');
        break;
      default:
        showToast('Chat connection error');
    }
  };
};

Parameters

| Parameter | Type | Required | Description | | ------------------------ | ------------------------------------ | -------- | ------------------------ | | collection | string | ✅ | Collection name | | onData | (data: DocumentData<T>[]) => void | ✅ | Data callback | | onError | (error: FirebaseHookError) => void | ✅ | Error handler (required) | | where | [string, WhereFilterOp, unknown][] | ❌ | Where conditions | | orderBy | [string, 'asc' \| 'desc'][] | ❌ | Sort conditions | | limit | number | ❌ | Results limit | | includeMetadataChanges | boolean | ❌ | Include metadata changes | | firebaseProject | string | ❌ | Firebase project name |

Real-time Document Monitoring

import {
  useDocumentListener,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const UserProfileLive = ({ userId }) => {
  const { listenToDocument } = useDocumentListener();
  const [user, setUser] = useState(null);

  useEffect(() => {
    const unsubscribe = listenToDocument({
      collection: 'users',
      doc: userId,
      includeMetadataChanges: true,

      onData: (docData) => {
        if (docData.exists) {
          setUser({
            id: docData.id,
            ...docData.data,
            isOnline: !docData.fromCache,
            hasUnsavedChanges: docData.hasPendingWrites,
          });
        } else {
          setUser(null);
        }
      },

      onError: (error) => handleDocListenerError(error, userId),
    });

    return () => unsubscribe();
  }, [userId]);

  const handleDocListenerError = (error, userId) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_DOCUMENT_EMPTY:
        console.error('User ID is required');
        break;
      case ERROR_CODES.LISTENER_SETUP_FAILED:
        showToast('Failed to connect to user profile');
        break;
      case ERROR_CODES.FIREBASE_DOCUMENT_NOT_FOUND:
        showToast('User profile not found');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Access denied to user profile');
        break;
      default:
        showToast('Profile connection error');
    }
  };
};

Parameters

| Parameter | Type | Required | Description | | ------------------------ | ------------------------------------ | -------- | ------------------------ | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | onData | (data: DocumentData<T>) => void | ✅ | Data callback | | onError | (error: FirebaseHookError) => void | ✅ | Error handler (required) | | includeMetadataChanges | boolean | ❌ | Include metadata changes | | firebaseProject | string | ❌ | Firebase project name |

Atomic Batch Operations

import {
  useFirestoreTransaction,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const TransferFunds = () => {
  const { executeBatch, executeTransaction } = useFirestoreTransaction();

  const batchUpdateUsers = async () => {
    const operations = [
      {
        type: 'set',
        collection: 'users',
        doc: 'user1',
        data: { name: 'John Doe', status: 'active' },
        merge: true,
        addTimestamp: true,
      },
      {
        type: 'update',
        collection: 'users',
        doc: 'user2',
        data: { lastLogin: new Date() },
        addTimestamp: true,
      },
      {
        type: 'delete',
        collection: 'users',
        doc: 'user3',
      },
    ];

    const docIds = await executeBatch({
      operations,
      onError: (error) => handleBatchError(error),
    });

    if (docIds) {
      showToast('Batch operation completed successfully');
    }
  };

  const transferFunds = async (fromUserId, toUserId, amount) => {
    const result = await executeTransaction({
      callback: async (transaction, firestore) => {
        const fromRef = firestore().collection('accounts').doc(fromUserId);
        const toRef = firestore().collection('accounts').doc(toUserId);

        const fromDoc = await transaction.get(fromRef);
        const toDoc = await transaction.get(toRef);

        if (!fromDoc.exists || !toDoc.exists) {
          throw new Error('Account not found');
        }

        const fromBalance = fromDoc.data().balance;
        const toBalance = toDoc.data().balance;

        if (fromBalance < amount) {
          throw new Error('Insufficient funds');
        }

        transaction.update(fromRef, { balance: fromBalance - amount });
        transaction.update(toRef, { balance: toBalance + amount });

        return {
          fromBalance: fromBalance - amount,
          toBalance: toBalance + amount,
        };
      },
      onError: (error) => handleTransactionError(error),
    });

    if (result) {
      showToast('Transfer completed successfully');
    }
  };

  const handleBatchError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_OPERATIONS_EMPTY:
        showToast('No operations provided');
        break;
      case ERROR_CODES.VALIDATION_OPERATIONS_LIMIT_EXCEEDED:
        showToast('Too many operations (max 500)');
        break;
      case ERROR_CODES.VALIDATION_OPERATION_TYPE_INVALID:
        showToast('Invalid operation type');
        break;
      case ERROR_CODES.OPERATION_BATCH_FAILED:
        showToast('Batch operation failed');
        break;
      default:
        showToast('Batch operation error');
    }
  };

  const handleTransactionError = (error) => {
    switch (error.code) {
      case ERROR_CODES.OPERATION_TRANSACTION_FAILED:
        showToast('Transaction failed - all changes reverted');
        break;
      case ERROR_CODES.FIREBASE_ABORTED:
        showToast('Transaction was aborted - please retry');
        break;
      default:
        showToast('Transaction error');
    }
  };
};

Batch Parameters

| Parameter | Type | Required | Description | | ----------------- | ------------------------------------ | -------- | ----------------------------- | | operations | BatchOperation[] | ✅ | Array of operations (max 500) | | firebaseProject | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

BatchOperation Interface

interface BatchOperation<T = unknown> {
  type: 'set' | 'update' | 'delete'; // Operation type
  collection: string; // Collection name
  doc: string; // Document ID
  data?: T; // Data (required for set/update)
  merge?: boolean; // Merge for set operations
  addTimestamp?: boolean; // Add server timestamp
}

Transaction Parameters

| Parameter | Type | Required | Description | | ----------------- | ---------------------------------------- | -------- | --------------------- | | callback | (transaction, firestore) => Promise<T> | ✅ | Transaction function | | firebaseProject | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |

Complete Batch Operations Example

const ComplexBatchOperations = () => {
  const { executeBatch } = useFirestoreTransaction();

  const performComplexUpdate = async () => {
    const operations = [
      // Set operation with merge
      {
        type: 'set',
        collection: 'users',
        doc: 'user1',
        data: {
          name: 'John Doe',
          email: '[email protected]',
          role: 'admin',
        },
        merge: true,
        addTimestamp: true,
      },
      // Update operation
      {
        type: 'update',
        collection: 'profiles',
        doc: 'profile1',
        data: {
          lastActive: new Date(),
          status: 'online',
        },
        addTimestamp: true,
      },
      // Delete operation
      {
        type: 'delete',
        collection: 'temp_data',
        doc: 'temp1',
      },
    ];

    const docIds = await executeBatch({
      operations,
      firebaseProject: 'production', // Optional project
      onError: (error) => handleBatchError(error),
    });

    if (docIds) {
      showToast(`Batch completed: ${docIds.length} operations`);
    }
  };

  const handleBatchError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_OPERATIONS_EMPTY:
        showToast('No operations provided');
        break;
      case ERROR_CODES.VALIDATION_OPERATIONS_LIMIT_EXCEEDED:
        showToast('Too many operations (max 500 per batch)');
        break;
      case ERROR_CODES.VALIDATION_OPERATION_TYPE_INVALID:
        showToast('Invalid operation type specified');
        break;
      case ERROR_CODES.VALIDATION_COLLECTION_EMPTY:
        showToast('Collection name missing in operation');
        break;
      case ERROR_CODES.VALIDATION_DOCUMENT_EMPTY:
        showToast('Document ID missing in operation');
        break;
      case ERROR_CODES.VALIDATION_DATA_INVALID:
        showToast('Invalid data for set/update operation');
        break;
      case ERROR_CODES.VALIDATION_DATA_EMPTY:
        showToast('Empty data for set/update operation');
        break;
      case ERROR_CODES.OPERATION_BATCH_FAILED:
        showToast('Batch operation failed');
        break;
      default:
        showToast('Batch operation error');
    }
  };
};

Advanced Transaction Example

const AdvancedTransaction = () => {
  const { executeTransaction } = useFirestoreTransaction();

  const complexTransaction = async () => {
    const result = await executeTransaction({
      callback: async (transaction, firestore) => {
        // Access multiple documents
        const userRef = firestore().collection('users').doc('user123');
        const accountRef = firestore().collection('accounts').doc('acc123');
        const logRef = firestore().collection('logs').doc(); // Auto-generated ID

        // Read operations (must be done first in transactions)
        const userDoc = await transaction.get(userRef);
        const accountDoc = await transaction.get(accountRef);

        if (!userDoc.exists || !accountDoc.exists) {
          throw new Error('Required documents not found');
        }

        const userData = userDoc.data();
        const accountData = accountDoc.data();

        // Validation logic
        if (userData.status !== 'active') {
          throw new Error('User account is not active');
        }

        if (accountData.balance < 100) {
          throw new Error('Insufficient funds');
        }

        // Write operations
        transaction.update(userRef, {
          lastTransaction: new Date(),
          transactionCount: (userData.transactionCount || 0) + 1,
        });

        transaction.update(accountRef, {
          balance: accountData.balance - 100,
          lastDebit: new Date(),
        });

        transaction.set(logRef, {
          userId: 'user123',
          action: 'debit',
          amount: 100,
          timestamp: new Date(),
          balanceAfter: accountData.balance - 100,
        });

        return {
          transactionId: logRef.id,
          newBalance: accountData.balance - 100,
          success: true,
        };
      },
      firebaseProject: 'main',
      onError: (error) => handleTransactionError(error),
    });

    if (result) {
      showToast(`Transaction completed: ${result.transactionId}`);
      return result;
    }
  };

  const handleTransactionError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_CALLBACK_MISSING:
        console.error('Transaction callback is required');
        break;
      case ERROR_CODES.OPERATION_TRANSACTION_FAILED:
        showToast('Transaction failed - all changes reverted');
        break;
      case ERROR_CODES.FIREBASE_ABORTED:
        showToast('Transaction was aborted - please retry');
        break;
      case ERROR_CODES.FIREBASE_FAILED_PRECONDITION:
        showToast('Transaction failed due to precondition');
        break;
      default:
        showToast('Transaction error occurred');
    }
  };
};

Getting Document References

import React from 'react';
import {
  useFirestoreRef,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const DocumentOperations = () => {
  const { getFirestoreReference } = useFirestoreRef();

  const getDocumentRef = (collection, docId, project) => {
    try {
      const docRef = getFirestoreReference(collection, docId, project);
      return docRef;
    } catch (error) {
      handleRefError(error);
      return null;
    }
  };

  const handleRefError = (error) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_COLLECTION_EMPTY:
        showToast('Collection name is required');
        break;
      case ERROR_CODES.VALIDATION_DOCUMENT_EMPTY:
        showToast('Document ID is required');
        break;
      case ERROR_CODES.VALIDATION_FIELD_INVALID:
        showToast('Invalid collection or document format');
        break;
      case ERROR_CODES.OPERATION_REFERENCE_FAILED:
        showToast('Failed to create document reference');
        break;
      default:
        showToast('Reference creation error');
    }
  };

  // Example usage in component
  const createUserReference = () => {
    const userRef = getDocumentRef('users', 'user123', 'production');
    if (userRef) {
      console.log('Reference created:', userRef.path);
    }
  };

  return (
    <View>
      <TouchableOpacity onPress={createUserReference}>
        <Text>Create User Reference</Text>
      </TouchableOpacity>
    </View>
  );
};

Reference Validation Rules

The hook performs comprehensive validation on collection and document names:

Collection Name Validation

  • ✅ Cannot be empty or whitespace only
  • ✅ Cannot contain double forward slashes (//)
  • ✅ Cannot start with forward slash (/)
  • ✅ Cannot end with forward slash (/)

Document ID Validation

  • ✅ Cannot be empty or whitespace only
  • ✅ Cannot contain forward slashes (/)

Advanced Reference Operations

const AdvancedReferenceUsage = () => {
  const { getFirestoreReference } = useFirestoreRef();

  const validateAndCreateRef = (collection, docId) => {
    try {
      // The hook automatically validates format
      const ref = getFirestoreReference(collection, docId);

      // Use reference for operations
      console.log('Reference path:', ref.path);
      console.log('Collection ID:', ref.parent.id);
      console.log('Document ID:', ref.id);

      return ref;
    } catch (error) {
      // Handle validation errors
      if (error.code === ERROR_CODES.VALIDATION_FIELD_INVALID) {
        if (error.message.includes('forward slashes')) {
          showToast('Document ID cannot contain "/" characters');
        } else if (error.message.includes('invalid characters')) {
          showToast('Collection name has invalid format');
        }
      }
      return null;
    }
  };

  // Examples of invalid references that will be caught
  const testInvalidReferences = () => {
    // These will all throw validation errors:

    // Invalid collection names
    // validateAndCreateRef('', 'doc1');           // Empty collection
    // validateAndCreateRef('/users', 'doc1');     // Starts with /
    // validateAndCreateRef('users/', 'doc1');     // Ends with /
    // validateAndCreateRef('users//posts', 'doc1'); // Contains //

    // Invalid document IDs
    // validateAndCreateRef('users', '');          // Empty doc ID
    // validateAndCreateRef('users', 'user/123');  // Contains /
  };

  return (
    <View>
      <TouchableOpacity onPress={() => validateAndCreateRef('users', 'user123')}>
        <Text>Create Valid Reference</Text>
      </TouchableOpacity>
    </View>
  );
};

Parameters

| Parameter | Type | Required | Description | | --------------------- | -------- | -------- | --------------------- | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | firebaseProjectName | string | ❌ | Firebase project name |

Document Existence Checking

import {
  useIsDocumentExist,
  ERROR_CODES,
} from 'akshay-khapare-react-native-firebase-hooks';

const DocumentChecker = () => {
  const { isExist } = useIsDocumentExist();

  const checkUserExists = async (userId) => {
    const exists = await isExist({
      collection: 'users',
      doc: userId,
      source: 'server', // Force server check
      onError: (error) => handleExistsError(error, userId),
    });

    if (exists !== null) {
      return exists ? 'User exists' : 'User not found';
    }
    return 'Check failed';
  };

  const handleExistsError = (error, userId) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_SOURCE_INVALID:
        showToast('Invalid data source specified');
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Access denied');
        break;
      case ERROR_CODES.OPERATION_GET_FAILED:
        showToast('Failed to check user existence');
        logError('Existence check failed', { userId, error });
        break;
      default:
        showToast('Existence check error');
    }
  };
};

Parameters

| Parameter | Type | Required | Description | | ----------------- | ------------------------------------ | -------- | --------------------- | | collection | string | ✅ | Collection name | | doc | string | ✅ | Document ID | | source | 'default' \| 'server' \| 'cache' | ❌ | Data source | | firebaseProject | string | ❌ | Firebase project name | | onError | (error: FirebaseHookError) => void | ❌ | Error handler |


🚨 Complete Error Handling Guide

Error Categories

import {
  ERROR_CATEGORIES,
  ERROR_CODES,
  ERROR_CODE_NUMBERS,
} from 'akshay-khapare-react-native-firebase-hooks';

// Available categories
ERROR_CATEGORIES.VALIDATION; // Input validation errors
ERROR_CATEGORIES.FIREBASE; // Firebase-specific errors
ERROR_CATEGORIES.NETWORK; // Network connectivity errors
ERROR_CATEGORIES.INITIALIZATION; // Setup and config errors
ERROR_CATEGORIES.OPERATION; // CRUD operation errors
ERROR_CATEGORIES.LISTENER; // Real-time listener errors

Validation Errors (1000-1999)

| Code | Number | Description | | -------------------------------------- | ------ | -------------------------- | | VALIDATION_COLLECTION_EMPTY | 1001 | Collection name is empty | | VALIDATION_DOCUMENT_EMPTY | 1002 | Document ID is empty | | VALIDATION_DATA_INVALID | 1003 | Invalid data format | | VALIDATION_DATA_EMPTY | 1004 | Data object is empty | | VALIDATION_CALLBACK_MISSING | 1005 | Required callback missing | | VALIDATION_CALLBACK_INVALID | 1006 | Invalid callback function | | VALIDATION_FIELD_MISSING | 1007 | Required field missing | | VALIDATION_FIELD_INVALID | 1008 | Invalid field format | | VALIDATION_LIMIT_INVALID | 1009 | Invalid limit value | | VALIDATION_OPERATIONS_EMPTY | 1010 | Operations array is empty | | VALIDATION_OPERATIONS_LIMIT_EXCEEDED | 1011 | Too many operations (>500) | | VALIDATION_OPERATION_TYPE_INVALID | 1012 | Invalid operation type | | VALIDATION_WHERE_CLAUSE_INVALID | 1013 | Invalid where condition | | VALIDATION_ORDER_BY_INVALID | 1014 | Invalid orderBy condition | | VALIDATION_PROJECT_NAME_EMPTY | 1015 | Project name is empty | | VALIDATION_CONFIG_MISSING | 1016 | Configuration missing | | VALIDATION_CONFIG_FIELD_MISSING | 1017 | Config field missing | | VALIDATION_SOURCE_INVALID | 1018 | Invalid data source |

Firebase Errors (2000-2999)

| Code | Number | Description | | ------------------------------- | ------ | ---------------------------- | | FIREBASE_APP_NOT_INITIALIZED | 2001 | Firebase app not initialized | | FIREBASE_PROJECT_NOT_FOUND | 2002 | Firebase project not found | | FIREBASE_DOCUMENT_NOT_FOUND | 2003 | Document not found | | FIREBASE_COLLECTION_NOT_FOUND | 2004 | Collection not found | | FIREBASE_PERMISSION_DENIED | 2005 | Permission denied | | FIREBASE_QUOTA_EXCEEDED | 2006 | Quota exceeded | | FIREBASE_INVALID_ARGUMENT | 2007 | Invalid argument | | FIREBASE_FAILED_PRECONDITION | 2008 | Failed precondition | | FIREBASE_ABORTED | 2009 | Operation aborted | | FIREBASE_OUT_OF_RANGE | 2010 | Out of range | | FIREBASE_UNIMPLEMENTED | 2011 | Unimplemented | | FIREBASE_INTERNAL | 2012 | Internal error | | FIREBASE_UNAVAILABLE | 2013 | Service unavailable | | FIREBASE_DATA_LOSS | 2014 | Data loss | | FIREBASE_UNAUTHENTICATED | 2015 | User not authenticated |

Network Errors (3000-3999)

| Code | Number | Description | | --------------------------- | ------ | ------------------- | | NETWORK_UNAVAILABLE | 3001 | Network unavailable | | NETWORK_TIMEOUT | 3002 | Network timeout | | NETWORK_CONNECTION_FAILED | 3003 | Connection failed | | NETWORK_DNS_FAILURE | 3004 | DNS failure |

Initialization Errors (4000-4999)

| Code | Number | Description | | ------------------------- | ------ | ------------------------------- | | INIT_FIREBASE_FAILED | 4001 | Firebase initialization failed | | INIT_FIRESTORE_FAILED | 4002 | Firestore initialization failed | | INIT_CONFIG_INVALID | 4003 | Invalid configuration | | INIT_PROJECT_EXISTS | 4004 | Project already exists | | INIT_CACHE_SIZE_INVALID | 4005 | Invalid cache size |

Operation Errors (5000-5999)

| Code | Number | Description | | ------------------------------ | ------ | ------------------------- | | OPERATION_SET_FAILED | 5001 | Set operation failed | | OPERATION_GET_FAILED | 5002 | Get operation failed | | OPERATION_UPDATE_FAILED | 5003 | Update operation failed | | OPERATION_DELETE_FAILED | 5004 | Delete operation failed | | OPERATION_QUERY_FAILED | 5005 | Query operation failed | | OPERATION_BATCH_FAILED | 5006 | Batch operation failed | | OPERATION_TRANSACTION_FAILED | 5007 | Transaction failed | | OPERATION_REFERENCE_FAILED | 5008 | Reference creation failed |

Listener Errors (6000-6999)

| Code | Number | Description | | --------------------------------- | ------ | ---------------------- | | LISTENER_SETUP_FAILED | 6001 | Listener setup failed | | LISTENER_DATA_PROCESSING_FAILED | 6002 | Data processing failed | | LISTENER_UNSUBSCRIBE_FAILED | 6003 | Unsubscribe failed | | LISTENER_SNAPSHOT_ERROR | 6004 | Snapshot error |

Unknown Errors (9000-9999)

| Code | Number | Description | | --------------- | ------ | ---------------------- | | UNKNOWN_ERROR | 9001 | Unknown error occurred |

Global Error Handler

import {
  ERROR_CODES,
  ERROR_CATEGORIES,
} from 'akshay-khapare-react-native-firebase-hooks';

const globalErrorHandler = (error) => {
  // Log to analytics
  logToAnalytics('firebase_error', {
    code: error.code,
    codeNumber: error.codeNumber,
    category: error.category,
    message: error.message,
    timestamp: error.timestamp,
    details: error.details,
  });

  // Handle by category
  switch (error.category) {
    case ERROR_CATEGORIES.VALIDATION:
      showToast('Please check your input and try again');
      break;
    case ERROR_CATEGORIES.FIREBASE:
      handleFirebaseError(error);
      break;
    case ERROR_CATEGORIES.NETWORK:
      showToast('Network error. Please check your connection.');
      break;
    case ERROR_CATEGORIES.OPERATION:
      showToast('Operation failed. Please try again.');
      break;
    default:
      showToast('An unexpected error occurred');
  }
};

const handleFirebaseError = (error) => {
  switch (error.code) {
    case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
      redirectToLogin();
      break;
    case ERROR_CODES.FIREBASE_QUOTA_EXCEEDED:
      showMaintenanceMessage();
      break;
    case ERROR_CODES.FIREBASE_UNAUTHENTICATED:
      refreshAuth();
      break;
    default:
      showToast('Firebase service error');
  }
};

Error Retry Logic

const retryableErrorCodes = [
  ERROR_CODES.NETWORK_UNAVAILABLE,
  ERROR_CODES.FIREBASE_UNAVAILABLE,
  ERROR_CODES.FIREBASE_ABORTED,
];

const withRetry = async (operation, maxRetries = 3) => {
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await operation({
        onError: (error) => {
          lastError = error;
          if (!retryableErrorCodes.includes(error.code)) {
            throw error; // Don't retry non-retryable errors
          }
        },
      });

      if (result !== null) return result; // Success

      if (
        attempt < maxRetries &&
        retryableErrorCodes.includes(lastError?.code)
      ) {
        await delay(Math.pow(2, attempt) * 1000); // Exponential backoff
        continue;
      }

      throw lastError;
    } catch (error) {
      if (attempt === maxRetries) throw error;
    }
  }
};

// Usage
const data = await withRetry(() =>
  getData({
    collection: 'users',
    doc: 'user123',
  })
);

Production Error Reporting

const productionErrorHandler = (error) => {
  // Send to crash reporting service
  crashAnalytics.recordError(error, {
    errorCode: error.code,
    errorNumber: error.codeNumber,
    category: error.category,
    context: error.details,
  });

  // Send to custom logging service
  logService.error('Firebase Hook Error', {
    code: error.code,
    message: error.message,
    timestamp: error.timestamp,
    userID: getCurrentUserId(),
    sessionID: getSessionId(),
    appVersion: getAppVersion(),
    details: error.details,
  });

  // Update error metrics
  errorMetrics.increment(`firebase_error.${error.category}.${error.code}`);
};

🔧 Advanced Usage Examples

Smart Data Loading Component

import { useFirestoreGet, useCollectionListener, ERROR_CODES } from 'akshay-khapare-react-native-firebase-hooks';

const SmartDataLoader = ({ collection, doc, realTime = false }) => {
  const { getData } = useFirestoreGet();
  const { listenToCollection } = useCollectionListener();
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const handleError = useCallback((error) => {
    setError(error);
    setLoading(false);

    // Production error handling
    switch (error.code) {
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        trackEvent('access_denied', { collection, doc });
        break;
      case ERROR_CODES.FIREBASE_QUOTA_EXCEEDED:
        trackEvent('quota_exceeded', { collection });
        notifyDevTeam('Quota exceeded', error.details);
        break;
      default:
        trackEvent('data_load_error', {
          collection,
          doc,
          errorCode: error.code
        });
    }
  }, [collection, doc]);

  useEffect(() => {
    if (realTime && !doc) {
      // Real-time collection
      const unsubscribe = listenToCollection({
        collection,
        onData: (data) => {
          setData(data);
          setLoading(false);
          setError(null);
        },
        onError: handleError
      });
      return unsubscribe;
    } else if (doc) {
      // Single document fetch
      const fetchData = async () => {
        setLoading(true);
        const result = await getData({
          collection,
          doc,
          onError: handleError
        });

        if (result) {
          setData(result);
          setLoading(false);
          setError(null);
        }
      };

      fetchData();
    }
  }, [collection, doc, realTime, getData, listenToCollection, handleError]);

  if (loading) return <LoadingSpinner />;
  if (error) return <ErrorDisplay error={error} onRetry={() => {
    setLoading(true);
    setError(null);
    // Trigger re-fetch by updating a state or calling fetch function again
  }} />;

  return <DataDisplay data={data} />;
};

Form with Auto-Save

const AutoSaveForm = ({ collection, doc, initialData }) => {
  const { setData } = useFirestoreSet();
  const { updateData } = useFirestoreUpdate();
  const [formData, setFormData] = useState(initialData);
  const [saveStatus, setSaveStatus] = useState('saved');

  const autoSave = useCallback(
    debounce(async (data) => {
      setSaveStatus('saving');

      const operation = doc ? updateData : setData;
      const result = await operation({
        collection,
        doc: doc || generateId(),
        data,
        addTimestamp: true,
        onError: (error) => {
          setSaveStatus('error');
          handleFormError(error, data);
        }
      });

      if (result) {
        setSaveStatus('saved');
        trackEvent('auto_save_success', { collection, doc });
      }
    }, 2000),
    [collection, doc, setData, updateData]
  );

  const handleFormError = (error, data) => {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_DATA_EMPTY:
        // Don't show error for empty auto-save
        break;
      case ERROR_CODES.FIREBASE_PERMISSION_DENIED:
        showToast('Permission denied. Changes not saved.');
        break;
      case ERROR_CODES.FIREBASE_QUOTA_EXCEEDED:
        showToast('Storage limit reached. Please contact support.');
        break;
      default:
        showToast('Auto-save failed. Your changes may be lost.');
        // Backup to AsyncStorage (React Native) or SecureStore
        AsyncStorage.setItem(`backup_${collection}_${doc}`, JSON.stringify(data));
    }
  };

  useEffect(() => {
    if (formData !== initialData) {
      autoSave(formData);
    }
  }, [formData, initialData, autoSave]);

  return (
    <View>
      <SaveStatus status={saveStatus} />
      {/* Form fields using TextInput, TouchableOpacity, etc. */}
    </View>
  );
};

📊 Performance & Best Practices

Hook Optimization Tips

  • Use source: 'cache' for better performance when appropriate
  • Implement proper cleanup for listeners to prevent memory leaks
  • Use includeMetadataChanges: false unless you need metadata updates
  • Implement pagination for large datasets using cursor-based queries
  • Use batch operations for multiple writes to improve performance

Error Handling Best Practices

  • Always provide onError callbacks in production
  • Implement retry logic for transient errors
  • Use error codes for specific user messaging
  • Log errors with context for debugging
  • Implement fallback UI states for error scenarios

Type Safety

  • Always use TypeScript interfaces for your data models
  • Leverage generic types for better type inference
  • Use strict type checking in your TypeScript config

🤝 Contributing

We welcome contributions! Please read our Contributing Guide for details.

📄 License

MIT © Akshay Khapare


🆘 Support


Made with ❤️ for the React Native community