React Native Firebase Cloud Firestore provides a NoSQL document database with real-time synchronization capabilities.
—
Real-time listeners for documents and collections with configurable options for metadata changes and error handling.
Listen to real-time changes on individual documents with multiple callback patterns.
/**
* Listen to document changes with observer pattern
* @param observer - Observer object with next, error, and complete handlers
* @returns Unsubscribe function to stop listening
*/
onSnapshot(observer: {
next?: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void;
error?: (error: Error) => void;
complete?: () => void;
}): () => void;
/**
* Listen to document changes with observer pattern and options
* @param options - Snapshot listen options
* @param observer - Observer object with handlers
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
options: FirebaseFirestoreTypes.SnapshotListenOptions,
observer: {
next?: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void;
error?: (error: Error) => void;
complete?: () => void;
}
): () => void;
/**
* Listen to document changes with callback functions
* @param onNext - Function called on each snapshot
* @param onError - Optional error handler
* @param onCompletion - Optional completion handler
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
onNext: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
/**
* Listen to document changes with options and callback functions
* @param options - Snapshot listen options
* @param onNext - Function called on each snapshot
* @param onError - Optional error handler
* @param onCompletion - Optional completion handler
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
options: FirebaseFirestoreTypes.SnapshotListenOptions,
onNext: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;Usage Examples:
import firestore from '@react-native-firebase/firestore';
const userDoc = firestore().collection('users').doc('userId');
// Basic listener with callback
const unsubscribe = userDoc.onSnapshot(snapshot => {
if (snapshot.exists) {
console.log('User data:', snapshot.data());
} else {
console.log('User does not exist');
}
});
// Listener with error handling
const unsubscribeWithError = userDoc.onSnapshot(
snapshot => {
console.log('User updated:', snapshot.data());
},
error => {
console.error('Error listening to user:', error);
}
);
// Observer pattern
const unsubscribeObserver = userDoc.onSnapshot({
next: snapshot => {
console.log('User data:', snapshot.data());
},
error: error => {
console.error('Listener error:', error);
},
complete: () => {
console.log('Listener completed');
}
});
// Include metadata changes
const unsubscribeWithMetadata = userDoc.onSnapshot(
{ includeMetadataChanges: true },
snapshot => {
console.log('Data:', snapshot.data());
console.log('From cache:', snapshot.metadata.fromCache);
console.log('Has pending writes:', snapshot.metadata.hasPendingWrites);
}
);
// Stop listening
unsubscribe();Listen to real-time changes on query results, receiving updates when documents are added, removed, or modified.
/**
* Listen to query changes with observer pattern
* @param observer - Observer object with next, error, and complete handlers
* @returns Unsubscribe function to stop listening
*/
onSnapshot(observer: {
next?: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void;
error?: (error: Error) => void;
complete?: () => void;
}): () => void;
/**
* Listen to query changes with observer pattern and options
* @param options - Snapshot listen options
* @param observer - Observer object with handlers
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
options: FirebaseFirestoreTypes.SnapshotListenOptions,
observer: {
next?: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void;
error?: (error: Error) => void;
complete?: () => void;
}
): () => void;
/**
* Listen to query changes with callback functions
* @param onNext - Function called on each snapshot
* @param onError - Optional error handler
* @param onCompletion - Optional completion handler
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
onNext: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;
/**
* Listen to query changes with options and callback functions
* @param options - Snapshot listen options
* @param onNext - Function called on each snapshot
* @param onError - Optional error handler
* @param onCompletion - Optional completion handler
* @returns Unsubscribe function to stop listening
*/
onSnapshot(
options: FirebaseFirestoreTypes.SnapshotListenOptions,
onNext: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void,
onError?: (error: Error) => void,
onCompletion?: () => void
): () => void;Usage Examples:
import firestore from '@react-native-firebase/firestore';
const usersQuery = firestore()
.collection('users')
.where('active', '==', true)
.orderBy('createdAt', 'desc');
// Basic query listener
const unsubscribe = usersQuery.onSnapshot(snapshot => {
console.log(`Found ${snapshot.size} active users`);
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
});
// Listen to document changes
const unsubscribeWithChanges = usersQuery.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
const doc = change.doc;
switch (change.type) {
case 'added':
console.log('New user:', doc.data());
break;
case 'modified':
console.log('Updated user:', doc.data());
break;
case 'removed':
console.log('Removed user:', doc.data());
break;
}
});
});
// Include metadata changes to detect local vs server updates
const unsubscribeMetadata = usersQuery.onSnapshot(
{ includeMetadataChanges: true },
snapshot => {
if (snapshot.metadata.fromCache) {
console.log('Data from cache');
} else {
console.log('Data from server');
}
snapshot.docChanges({ includeMetadataChanges: true }).forEach(change => {
if (change.doc.metadata.hasPendingWrites) {
console.log('Document has local changes');
}
});
}
);
// Observer pattern with error handling
const unsubscribeObserver = usersQuery.onSnapshot({
next: snapshot => {
console.log('Query updated:', snapshot.size, 'documents');
},
error: error => {
console.error('Query listener error:', error);
}
});
// Stop listening
unsubscribe();Listen for when all active listeners are in sync with the server.
/**
* Listen for snapshots in sync events with observer pattern
* @param observer - Observer object with handlers
* @returns Unsubscribe function to stop listening
*/
onSnapshotsInSync(observer: {
next?: () => void;
error?: (error: Error) => void;
complete?: () => void;
}): () => void;
/**
* Listen for snapshots in sync events with callback
* @param onSync - Function called when snapshots are in sync
* @returns Unsubscribe function to stop listening
*/
onSnapshotsInSync(onSync: () => void): () => void;Usage Examples:
import firestore from '@react-native-firebase/firestore';
// Basic sync listener
const unsubscribeSync = firestore().onSnapshotsInSync(() => {
console.log('All snapshots are now in sync with the server');
});
// Observer pattern
const unsubscribeSyncObserver = firestore().onSnapshotsInSync({
next: () => {
console.log('Snapshots in sync');
},
error: error => {
console.error('Sync error:', error);
}
});
// Stop listening
unsubscribeSync();Best practices for handling multiple real-time listeners efficiently.
Usage Examples:
import firestore from '@react-native-firebase/firestore';
class UserProfileComponent {
private unsubscribers: Array<() => void> = [];
componentDidMount() {
const userId = 'currentUserId';
// Listen to user profile
const userUnsubscribe = firestore()
.collection('users')
.doc(userId)
.onSnapshot(snapshot => {
this.setState({ user: snapshot.data() });
});
// Listen to user's posts
const postsUnsubscribe = firestore()
.collection('posts')
.where('authorId', '==', userId)
.orderBy('createdAt', 'desc')
.onSnapshot(snapshot => {
const posts = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
this.setState({ posts });
});
// Listen to notifications
const notificationsUnsubscribe = firestore()
.collection('notifications')
.where('userId', '==', userId)
.where('read', '==', false)
.onSnapshot(snapshot => {
this.setState({ unreadNotifications: snapshot.size });
});
// Store all unsubscribers
this.unsubscribers = [
userUnsubscribe,
postsUnsubscribe,
notificationsUnsubscribe
];
}
componentWillUnmount() {
// Clean up all listeners
this.unsubscribers.forEach(unsubscribe => unsubscribe());
this.unsubscribers = [];
}
}interface SnapshotListenOptions {
/**
* Whether to include metadata-only changes
* Includes changes to document metadata like pending writes or cache status
*/
includeMetadataChanges: boolean;
}
interface SnapshotMetadata {
/**
* True if the snapshot contains the result of local writes not yet committed to the backend
*/
readonly fromCache: boolean;
/**
* True if the snapshot includes local writes that have not yet been committed to the backend
*/
readonly hasPendingWrites: boolean;
/**
* Check if two metadata objects are equal
*/
isEqual(other: FirebaseFirestoreTypes.SnapshotMetadata): boolean;
}
interface DocumentChange<T = FirebaseFirestoreTypes.DocumentData> {
/**
* The document affected by this change
*/
readonly doc: FirebaseFirestoreTypes.QueryDocumentSnapshot<T>;
/**
* The new index of the changed document in the result set (-1 if removed)
*/
readonly newIndex: number;
/**
* The old index of the changed document in the result set (-1 if added)
*/
readonly oldIndex: number;
/**
* The type of change ('added', 'modified', or 'removed')
*/
readonly type: FirebaseFirestoreTypes.DocumentChangeType;
}
type DocumentChangeType = 'added' | 'removed' | 'modified';
/**
* Unsubscribe function returned by snapshot listeners
*/
type Unsubscribe = () => void;interface FirestoreError extends Error {
/**
* Error code indicating the type of error
*/
readonly code: FirebaseFirestoreTypes.FirestoreErrorCode;
/**
* Human-readable error message
*/
readonly message: string;
}
type FirestoreErrorCode =
| 'cancelled'
| 'unknown'
| 'invalid-argument'
| 'deadline-exceeded'
| 'not-found'
| 'already-exists'
| 'permission-denied'
| 'resource-exhausted'
| 'failed-precondition'
| 'aborted'
| 'out-of-range'
| 'unimplemented'
| 'internal'
| 'unavailable'
| 'data-loss'
| 'unauthenticated';Error Handling Examples:
import firestore from '@react-native-firebase/firestore';
const userDoc = firestore().collection('users').doc('userId');
const unsubscribe = userDoc.onSnapshot(
snapshot => {
// Handle successful snapshot
console.log('User data:', snapshot.data());
},
error => {
// Handle errors
switch (error.code) {
case 'permission-denied':
console.error('User does not have permission to access this document');
break;
case 'unavailable':
console.error('Service is currently unavailable');
break;
case 'unauthenticated':
console.error('User is not authenticated');
break;
default:
console.error('Unknown error:', error.message);
}
}
);Install with Tessl CLI
npx tessl i tessl/npm-react-native-firebase--firestore