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-expo-firebase-hooks

v1.0.2

Published

Production-ready Firebase hooks for Expo React Native applications with comprehensive error handling

Downloads

14

Readme

akshay-khapare-expo-firebase-hooks

Production-ready Firebase hooks for Expo React Native applications with comprehensive error handling and TypeScript support.

🚀 Features

  • Simple & Direct: Clean API with direct error throwing and data returns
  • Production Ready: Optimized for real-world applications
  • Type Safe: Full TypeScript support with function-level generics
  • Comprehensive Error Handling: All Firebase errors are properly caught and thrown with detailed messages
  • Modern React: Uses latest React hooks patterns (no useCallback - add in parent as needed)
  • Expo Compatible: Designed specifically for Expo React Native
  • Performance Optimized: Efficient listeners and memory management
  • Dual Approach: Direct returns for operations, callbacks for real-time listeners

📦 Installation

npm install akshay-khapare-expo-firebase-hooks

Peer Dependencies

npm install react react-native firebase

🔧 Setup

Initialize Firebase once in your app:

import { firebaseInit } from 'akshay-khapare-expo-firebase-hooks';

firebaseInit({
  projects: [
    {
      projectName: 'main-project',
      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:abcdef123456',
      },
    },
  ],
});

🎯 Quick Start Example

interface User {
  id: string;
  name: string;
  email: string;
  status: 'online' | 'offline';
}

function MyComponent() {
  const { getDocument } = useFirestoreGetDoc();
  const { addDocument } = useFirestoreAdd();
  const [loading, setLoading] = useState(false);

  const handleGetUser = async () => {
    try {
      setLoading(true);
      const user = await getDocument<User>({
      projectName: 'main-project',
      collectionName: 'users',
      documentId: 'user123',
      });
      console.log('User:', user?.name);
    } catch (error) {
      console.error('Error:', error.message);
    } finally {
      setLoading(false);
    }
  };

  const handleAddUser = async () => {
    try {
      const result = await addDocument({
        projectName: 'main-project',
        collectionName: 'users',
        data: { name: 'John', email: '[email protected]' },
      });
      console.log('Added user with ID:', result.id);
      Alert.alert('Success', `User created with ID: ${result.id}`);
    } catch (error) {
      if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Error', 'You do not have permission to add users.');
      } else {
        Alert.alert('Error', 'Failed to add user: ' + error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button title="Get User" onPress={handleGetUser} />
      <Button title="Add User" onPress={handleAddUser} />
    </View>
  );
}

🎣 Available Hooks

📝 Document Operations

Add documents to any Firestore collection. Returns the document ID directly.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • data: Record<string, unknown> - Document data to add

Returns: Promise<{ id: string }> - The newly created document ID

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName or data are invalid.
  • Permission Denied: User does not have permission to write to the specified collection.
  • Network Errors: Device is offline or has unstable connectivity.
import { useFirestoreAdd, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function AddUserComponent() {
  const { addDocument } = useFirestoreAdd();
  const [loading, setLoading] = useState(false);

  const handleAddUser = async () => {
    try {
      setLoading(true);
      const result = await addDocument({
      projectName: 'main-project',
      collectionName: 'users',
      data: {
        name: 'John',
        email: '[email protected]',
        status: 'online',
        createdAt: new Date().toISOString(),
      },
      });

      console.log('User added with ID:', result.id);
      Alert.alert('Success', `User created with ID: ${result.id}`);
    } catch (error) {
      if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Error', 'You do not have permission to add users.');
      } else {
        Alert.alert('Error', 'Failed to add user: ' + error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <TouchableOpacity onPress={handleAddUser} disabled={loading}>
      <Text>{loading ? 'Adding...' : 'Add User'}</Text>
    </TouchableOpacity>
  );
}

Error Handling:

  • Parameter validation errors (missing/invalid data)
  • Permission denied errors
  • Network connectivity issues
  • Firestore service unavailable
  • Invalid document structure

Set documents with specific IDs (creates or overwrites). No return value.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • documentId: string - Document ID to set
  • data: Record<string, unknown> - Document data
  • merge: boolean (optional) - Whether to merge with existing data (default: false)

Returns: Promise<void>

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName, documentId, or data are invalid (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to write to the specified document (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreSet, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function SetUserComponent() {
  const { setDocument } = useFirestoreSet();
  const [isUpdating, setIsUpdating] = useState(false);

  const handleSetUser = async () => {
    try {
      setIsUpdating(true);
      await setDocument({
      projectName: 'main-project',
      collectionName: 'users',
      documentId: 'user123',
        data: {
          name: 'John Updated',
          email: '[email protected]',
          updatedAt: new Date().toISOString(),
        },
        merge: true, // Merge with existing data
      });

      Alert.alert('Success', 'User updated successfully!');
    } catch (error) {
      if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Error', 'You are not allowed to edit this user.');
      } else {
        Alert.alert('Error', 'Failed to update user: ' + error.message);
      }
    } finally {
      setIsUpdating(false);
    }
  };

  return (
    <Button
      title={isUpdating ? "Updating..." : "Set User"}
      onPress={handleSetUser}
      disabled={isUpdating}
    />
  );
}

Update existing documents. Only updates specified fields.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • documentId: string - Document ID to update
  • data: Record<string, unknown> - Fields to update

Returns: Promise<void>

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Document Not Found: The hook will throw an error if the document at the specified path does not exist (DOCUMENT_NOT_FOUND).
  • Parameter Validation: Throws if collectionName, documentId, or data are invalid (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to update the specified document (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreUpdate, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function UpdateUserComponent() {
  const { updateDocument } = useFirestoreUpdate();
  const [loading, setLoading] = useState(false);

  const handleUpdateUserStatus = async (status: 'online' | 'offline') => {
    try {
      setLoading(true);
      await updateDocument({
      projectName: 'main-project',
      collectionName: 'users',
      documentId: 'user123',
        data: {
          status,
          lastSeen: new Date().toISOString(),
      },
    });

      console.log('User status updated to:', status);
    } catch (error) {
      if (error.code === FirebaseErrorCode.DOCUMENT_NOT_FOUND) {
        Alert.alert('Not Found', 'This user does not exist and cannot be updated.');
      } else {
        Alert.alert('Update Error', error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button
        title="Set Online"
        onPress={() => handleUpdateUserStatus('online')}
        disabled={loading}
      />
      <Button
        title="Set Offline"
        onPress={() => handleUpdateUserStatus('offline')}
        disabled={loading}
      />
    </View>
  );
}

Delete documents from Firestore permanently.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • documentId: string - Document ID to delete

Returns: Promise<void>

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName or documentId are invalid (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to delete the specified document (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreDelete, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function DeleteUserComponent() {
  const { deleteDocument } = useFirestoreDelete();
  const [deleting, setDeleting] = useState(false);

  const handleDeleteUser = async (userId: string) => {
    try {
      // Confirm deletion
      Alert.alert(
        'Confirm Delete',
        'Are you sure you want to delete this user?',
        [
          { text: 'Cancel', style: 'cancel' },
          {
            text: 'Delete',
            style: 'destructive',
            onPress: async () => {
              try {
                setDeleting(true);
                await deleteDocument({
      projectName: 'main-project',
      collectionName: 'users',
                  documentId: userId,
                });

                Alert.alert('Success', 'User deleted successfully');
              } catch (error) {
                if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
                  Alert.alert('Permission Error', 'You do not have permission to delete this user.');
                } else {
                  Alert.alert('Delete Error', error.message);
                }
              } finally {
                setDeleting(false);
              }
            }
          }
        ]
      );
    } catch (error) {
      console.error('Delete failed:', error.message);
    }
  };

  return (
    <Button
      title={deleting ? "Deleting..." : "Delete User"}
      onPress={() => handleDeleteUser('user123')}
      disabled={deleting}
      color="red"
    />
  );
}

📖 Data Retrieval

Fetch a single document from Firestore. Returns the document data or null if not found.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • documentId: string - Document ID to fetch

Returns: Promise<T | null> - Document data with ID field added, or null if not found

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName or documentId are invalid (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to read from the specified location (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreGetDoc, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

interface User {
  id: string;
  name: string;
  email: string;
  status: 'online' | 'offline';
}

function GetUserComponent() {
  const { getDocument } = useFirestoreGetDoc();
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(false);

  const fetchUser = async (userId: string) => {
    try {
      setLoading(true);
      const userData = await getDocument<User>({
      projectName: 'main-project',
      collectionName: 'users',
      documentId: userId,
      });

        if (userData) {
        console.log('Found user:', userData.name);
          setUser(userData);
      } else {
        console.log('User not found');
        Alert.alert('Not Found', 'User does not exist');
      }
    } catch (error) {
      if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Error', 'You do not have access to this user\'s data.');
      } else {
        Alert.alert('Error', 'Failed to fetch user: ' + error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button
        title={loading ? "Loading..." : "Fetch User"}
        onPress={() => fetchUser('user123')}
        disabled={loading}
      />
      {user && (
    <View>
      <Text>Name: {user.name}</Text>
      <Text>Email: {user.email}</Text>
      <Text>Status: {user.status}</Text>
        </View>
      )}
    </View>
  );
}

Fetch multiple documents from a collection with optional query constraints.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • queries: QueryConstraint[] (optional) - Firestore query constraints

Returns: Promise<T[]> - Array of documents with ID fields added

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName is invalid (INVALID_ARGUMENT).
  • Invalid Queries: One of the provided queries is malformed (e.g., incorrect field path or operator) (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to query the collection (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreGetCollection, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';
import { where, orderBy, limit } from 'firebase/firestore';

interface User {
  id: string;
  name: string;
  email: string;
  status: 'online' | 'offline';
  createdAt: string;
}

function GetUsersComponent() {
  const { getCollection } = useFirestoreGetCollection();
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);

  const fetchOnlineUsers = async () => {
    try {
      setLoading(true);
      const userData = await getCollection<User>({
      projectName: 'main-project',
        collectionName: 'users',
      queries: [
          where('status', '==', 'online'),
        orderBy('createdAt', 'desc'),
          limit(10)
        ],
      });

      console.log('Found', userData.length, 'online users');
      setUsers(userData);
    } catch (error) {
      if (error.code === FirebaseErrorCode.INVALID_ARGUMENT) {
        Alert.alert('Query Error', 'There is an issue with the query constraints.');
      } else {
        Alert.alert('Error', 'Failed to fetch users: ' + error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  const fetchAllUsers = async () => {
    try {
      setLoading(true);
      const userData = await getCollection<User>({
      projectName: 'main-project',
      collectionName: 'users',
        // No queries = fetch all documents
      });

      setUsers(userData);
    } catch (error) {
      console.error('Fetch failed:', error.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button title="Fetch Online Users" onPress={fetchOnlineUsers} />
      <Button title="Fetch All Users" onPress={fetchAllUsers} />

      {loading && <Text>Loading...</Text>}

      <FlatList
        data={users}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View>
            <Text>{item.name} - {item.status}</Text>
          </View>
        )}
      />
    </View>
  );
}

Check if a document exists without fetching its data.

Parameters:

  • projectName: string - Firebase project identifier
  • collectionName: string - Firestore collection name
  • documentId: string - Document ID to check

Returns: Promise<boolean> - true if document exists, false otherwise

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Parameter Validation: Throws if collectionName or documentId are invalid (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission to access the document path (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreDocumentExists, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function CheckUserComponent() {
  const { checkDocumentExists } = useFirestoreDocumentExists();
  const [checking, setChecking] = useState(false);

  const checkUserExists = async (userId: string) => {
    try {
      setChecking(true);
      const exists = await checkDocumentExists({
      projectName: 'main-project',
      collectionName: 'users',
        documentId: userId,
      });

      if (exists) {
        Alert.alert('Found', 'User exists in database');
      } else {
        Alert.alert('Not Found', 'User does not exist');
      }
    } catch (error) {
      if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Error', 'You do not have permission to check this user.');
      } else {
        Alert.alert('Error', 'Failed to check user: ' + error.message);
      }
    } finally {
      setChecking(false);
    }
  };

  return (
    <Button
      title={checking ? "Checking..." : "Check User Exists"}
      onPress={() => checkUserExists('user123')}
      disabled={checking}
    />
  );
}

🔄 Real-time Listeners

Listens for real-time updates to a single document in a Firestore collection. The hook manages the subscription lifecycle and automatically unsubscribes on unmount.

Parameters

  • DocumentListenerParams<T>: An object containing:
    • projectName (string): The Firebase project name.
    • collectionName (string): The name of the collection.
    • documentId (string): The ID of the document to listen to.
    • onSuccess ((data: T | null) => void): Callback for successful updates. Receives the document data (with id) or null if it doesn't exist.
    • onError ((error: FirebaseHookError) => void): Callback for handling errors.

Returns

  • unsubscribe (function): A function that can be called to manually stop the listener.

Potential Errors (via onError callback)

  • Parameter Validation: Invalid collectionName or documentId (INVALID_ARGUMENT).
  • Permission Denied: The user does not have permission to listen to the specified document (PERMISSION_DENIED).
  • Network Errors: The device loses connectivity (NETWORK_ERROR).

Usage Example

import React, { useState, useEffect } from 'react';
import {
  useFirestoreDocumentListener,
  FirebaseErrorCode,
} from 'akshay-khapare-expo-firebase-hooks';

const UserProfile = ({ userId }) => {
  const [user, setUser] = useState(null);
  const [error, setError] = useState(null);

  const unsubscribe = useFirestoreDocumentListener({
    projectName: 'my-project',
    collectionName: 'users',
    documentId: userId,
    onSuccess: data => {
      console.log('Received real-time update:', data);
      setUser(data);
    },
    onError: err => {
      console.error('Listener Error:', err);
      if (err.code === FirebaseErrorCode.PERMISSION_DENIED) {
        setError('You do not have permission to view this profile.');
      } else {
        setError(err.message);
      }
    },
  });

  // You can manually unsubscribe if needed, e.g., on a button click
  // useEffect(() => {
  //   return () => unsubscribe();
  // }, [unsubscribe]);

  if (error) {
    return <p>Error: {error}</p>;
  }

  return (
    <div>
      <h2>User Profile (Real-time)</h2>
      {user ? (
        <pre>{JSON.stringify(user, null, 2)}</pre>
      ) : (
        <p>Loading user...</p>
      )}
    </div>
  );
};

Listens for real-time updates to a collection in Firestore, with optional queries. The hook manages the subscription lifecycle and automatically unsubscribes on unmount.

Parameters

  • CollectionListenerParams<T>: An object containing:
    • projectName (string): The Firebase project name.
    • collectionName (string): The name of the collection.
    • queries (QueryConstraint[] - optional): An array of Firestore queries (where, orderBy, limit).
    • onSuccess ((data: T[]) => void): Callback for successful updates. Receives an array of document data (with id).
    • onError ((error: FirebaseHookError) => void): Callback for handling errors.

Returns

  • unsubscribe (function): A function that can be called to manually stop the listener.

Potential Errors (via onError callback)

  • Parameter Validation: Invalid collectionName (INVALID_ARGUMENT).
  • Invalid Queries: A query constraint is malformed or references an index that does not exist (INVALID_ARGUMENT).
  • Permission Denied: The user does not have permission to listen to the specified collection (PERMISSION_DENIED).
  • Network Errors: The device loses connectivity (NETWORK_ERROR).

Usage Example

import React, { useState, useEffect } from 'react';
import {
  useFirestoreCollectionListener,
  FirebaseErrorCode,
} from 'akshay-khapare-expo-firebase-hooks';
import { where, orderBy } from 'firebase/firestore';

const ActiveUsersList = () => {
  const [users, setUsers] = useState([]);
  const [error, setError] = useState(null);

  const unsubscribe = useFirestoreCollectionListener({
    projectName: 'my-project',
    collectionName: 'users',
    queries: [where('status', '==', 'active'), orderBy('lastSeen', 'desc')],
    onSuccess: data => {
      console.log('Received real-time collection update:', data);
      setUsers(data);
    },
    onError: err => {
      console.error('Listener Error:', err);
      if (err.code === FirebaseErrorCode.PERMISSION_DENIED) {
        setError('You do not have permission to view this list of users.');
      } else if (err.code === FirebaseErrorCode.INVALID_ARGUMENT) {
        setError('The query is invalid. Please check the constraints.');
      } else {
        setError(err.message);
      }
    },
  });

  if (error) {
    return <p>Error: {error}</p>;
  }

  return (
    <div>
      <h2>Active Users (Real-time)</h2>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

⚡ Batch Operations

Execute multiple Firestore operations in a single atomic transaction.

Parameters:

  • operations: BatchOperation[] - Array of operations to execute

Returns: Promise<void>

Throws: FirebaseHookError - For any Firebase operation errors

Potential Errors:

  • Invalid Operations: The operations array is empty or an operation object is missing required fields (type, collectionName, etc.) (INVALID_ARGUMENT).
  • Error in Single Operation: The error message will specify the index of the operation that failed (e.g., due to invalid data or a non-existent document for an 'update' operation) (INVALID_ARGUMENT).
  • Permission Denied: User does not have permission for one of the operations in the batch (PERMISSION_DENIED).
  • Network Errors: Device is offline or has unstable connectivity (NETWORK_ERROR).
import { useFirestoreTransaction, FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';

function BatchOperationsComponent() {
  const { executeBatch } = useFirestoreTransaction();
  const [processing, setProcessing] = useState(false);

  const performBatchOperations = async () => {
    try {
      setProcessing(true);

      await executeBatch({
      operations: [
        {
          type: 'set',
          projectName: 'main-project',
          collectionName: 'users',
          documentId: 'user1',
            data: { name: 'John', status: 'online' },
            merge: true,
        },
        {
          type: 'update',
            projectName: 'main-project',
            collectionName: 'users',
            documentId: 'user2',
            data: { lastSeen: new Date().toISOString() },
        },
        {
          type: 'delete',
          projectName: 'main-project',
            collectionName: 'users',
            documentId: 'user3',
        },
      ],
      });

      Alert.alert('Success', 'Batch operations completed!');
    } catch (error) {
      if (error.code === FirebaseErrorCode.INVALID_ARGUMENT) {
        Alert.alert('Invalid Operation', error.message);
      } else if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
        Alert.alert('Permission Denied', 'You do not have permission for one of the operations in the batch.');
      } else {
        Alert.alert('Batch Error', error.message);
      }
    } finally {
      setProcessing(false);
    }
  };

  return (
    <Button
      title={processing ? "Processing..." : "Execute Batch"}
      onPress={performBatchOperations}
      disabled={processing}
    />
  );
}

🚨 Error Handling

All hooks use comprehensive error handling that covers:

Error Types

All thrown errors are instances of FirebaseHookError and will have a code property that corresponds to the FirebaseErrorCode enum. You should import and use this enum for type-safe error checking.

  • FirebaseErrorCode.INVALID_ARGUMENT: Missing or invalid parameters passed to a hook.
  • FirebaseErrorCode.PERMISSION_DENIED: User does not have permission according to Firestore security rules.
  • FirebaseErrorCode.NETWORK_ERROR: The device is offline or the connection is unstable.
  • FirebaseErrorCode.DOCUMENT_NOT_FOUND: A document specified for an update or delete operation does not exist.
  • FirebaseErrorCode.OPERATION_FAILED: A generic failure from the Firebase SDK.
  • FirebaseErrorCode.UNKNOWN_ERROR: An unexpected error occurred.

Error Structure

import { FirebaseErrorCode } from 'akshay-khapare-expo-firebase-hooks';
interface FirebaseHookError extends Error {
  message: string; // Human-readable error message
  code?: string; // Firebase error code
  originalError?: Error; // Original Firebase error object
}

Best Practices

  1. Always use try-catch for operations:
try {
  const result = await addDocument({ ... });
  // Handle success
} catch (error) {
  // Handle error
  console.error('Operation failed:', error.message);
}
  1. Check for specific error types:
catch (error) {
  if (error.code === FirebaseErrorCode.PERMISSION_DENIED) {
    Alert.alert('Access Denied', 'You don\'t have permission for this action');
  } else if (error.code === FirebaseErrorCode.DOCUMENT_NOT_FOUND) {
    Alert.alert('Not Found', 'The requested document doesn\'t exist');
  } else {
    Alert.alert('Error', error.message);
  }
}
  1. Use loading states:
const [loading, setLoading] = useState(false);

const handleOperation = async () => {
  setLoading(true);
  try {
    await performOperation();
  } finally {
    setLoading(false); // Always reset loading
  }
};
  1. Cleanup listeners:

The listener hooks (useFirestoreDocumentListener, useFirestoreCollectionListener) automatically clean up their subscriptions when the component unmounts. You can also call the returned unsubscribe function manually if you need to stop listening earlier.

useEffect(
  () => {
    const unsubscribe = useFirestoreDocumentListener({
      // ... params
    });

    // The listener is active.

    return () => {
      unsubscribe(); // This is called on unmount.
    };
  },
  [
    /* dependencies */
  ]
);

🔧 TypeScript Support

Full TypeScript support with generics for type-safe data handling:

interface User {
  id: string;
  name: string;
  email: string;
}

// Type-safe document retrieval
const user = await getDocument<User>({ ... }); // user is User | null

// Type-safe collection retrieval
const users = await getCollection<User>({ ... }); // users is User[]

// Type-safe listeners
useFirestoreDocumentListener<User>({
  onSuccess: (user) => {
    // user is typed as User | null
  }
});

📱 Performance Tips

  1. Add useCallback in parent components as needed:
const handleAddUser = useCallback(async () => {
  // Your operation
}, [dependencies]);
  1. Implement proper cleanup:

Listener hooks automatically handle cleanup. If you need to stop listening based on some application logic, you can call the unsubscribe function that the hook returns.

const unsubscribe = useFirestoreCollectionListener({ ... });

// Later, perhaps in a button press handler:
const handleStopListening = () => {
  unsubscribe();
};
  1. Use appropriate queries:
// Good: Limited results
const users = await getCollection({
  queries: [limit(20), where('active', '==', true)],
});

// Avoid: Fetching all documents
const users = await getCollection({});

📄 License

MIT License - see LICENSE file for details.

🤝 Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests.

🐛 Issues

Found a bug? Please open an issue with detailed reproduction steps.