0
# Real-time Data Synchronization
1
2
Real-time listeners for documents and collections with configurable options for metadata changes and error handling.
3
4
## Capabilities
5
6
### Document Listeners
7
8
Listen to real-time changes on individual documents with multiple callback patterns.
9
10
```typescript { .api }
11
/**
12
* Listen to document changes with observer pattern
13
* @param observer - Observer object with next, error, and complete handlers
14
* @returns Unsubscribe function to stop listening
15
*/
16
onSnapshot(observer: {
17
next?: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void;
18
error?: (error: Error) => void;
19
complete?: () => void;
20
}): () => void;
21
22
/**
23
* Listen to document changes with observer pattern and options
24
* @param options - Snapshot listen options
25
* @param observer - Observer object with handlers
26
* @returns Unsubscribe function to stop listening
27
*/
28
onSnapshot(
29
options: FirebaseFirestoreTypes.SnapshotListenOptions,
30
observer: {
31
next?: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void;
32
error?: (error: Error) => void;
33
complete?: () => void;
34
}
35
): () => void;
36
37
/**
38
* Listen to document changes with callback functions
39
* @param onNext - Function called on each snapshot
40
* @param onError - Optional error handler
41
* @param onCompletion - Optional completion handler
42
* @returns Unsubscribe function to stop listening
43
*/
44
onSnapshot(
45
onNext: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void,
46
onError?: (error: Error) => void,
47
onCompletion?: () => void
48
): () => void;
49
50
/**
51
* Listen to document changes with options and callback functions
52
* @param options - Snapshot listen options
53
* @param onNext - Function called on each snapshot
54
* @param onError - Optional error handler
55
* @param onCompletion - Optional completion handler
56
* @returns Unsubscribe function to stop listening
57
*/
58
onSnapshot(
59
options: FirebaseFirestoreTypes.SnapshotListenOptions,
60
onNext: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot<T>) => void,
61
onError?: (error: Error) => void,
62
onCompletion?: () => void
63
): () => void;
64
```
65
66
**Usage Examples:**
67
68
```typescript
69
import firestore from '@react-native-firebase/firestore';
70
71
const userDoc = firestore().collection('users').doc('userId');
72
73
// Basic listener with callback
74
const unsubscribe = userDoc.onSnapshot(snapshot => {
75
if (snapshot.exists) {
76
console.log('User data:', snapshot.data());
77
} else {
78
console.log('User does not exist');
79
}
80
});
81
82
// Listener with error handling
83
const unsubscribeWithError = userDoc.onSnapshot(
84
snapshot => {
85
console.log('User updated:', snapshot.data());
86
},
87
error => {
88
console.error('Error listening to user:', error);
89
}
90
);
91
92
// Observer pattern
93
const unsubscribeObserver = userDoc.onSnapshot({
94
next: snapshot => {
95
console.log('User data:', snapshot.data());
96
},
97
error: error => {
98
console.error('Listener error:', error);
99
},
100
complete: () => {
101
console.log('Listener completed');
102
}
103
});
104
105
// Include metadata changes
106
const unsubscribeWithMetadata = userDoc.onSnapshot(
107
{ includeMetadataChanges: true },
108
snapshot => {
109
console.log('Data:', snapshot.data());
110
console.log('From cache:', snapshot.metadata.fromCache);
111
console.log('Has pending writes:', snapshot.metadata.hasPendingWrites);
112
}
113
);
114
115
// Stop listening
116
unsubscribe();
117
```
118
119
### Query Listeners
120
121
Listen to real-time changes on query results, receiving updates when documents are added, removed, or modified.
122
123
```typescript { .api }
124
/**
125
* Listen to query changes with observer pattern
126
* @param observer - Observer object with next, error, and complete handlers
127
* @returns Unsubscribe function to stop listening
128
*/
129
onSnapshot(observer: {
130
next?: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void;
131
error?: (error: Error) => void;
132
complete?: () => void;
133
}): () => void;
134
135
/**
136
* Listen to query changes with observer pattern and options
137
* @param options - Snapshot listen options
138
* @param observer - Observer object with handlers
139
* @returns Unsubscribe function to stop listening
140
*/
141
onSnapshot(
142
options: FirebaseFirestoreTypes.SnapshotListenOptions,
143
observer: {
144
next?: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void;
145
error?: (error: Error) => void;
146
complete?: () => void;
147
}
148
): () => void;
149
150
/**
151
* Listen to query changes with callback functions
152
* @param onNext - Function called on each snapshot
153
* @param onError - Optional error handler
154
* @param onCompletion - Optional completion handler
155
* @returns Unsubscribe function to stop listening
156
*/
157
onSnapshot(
158
onNext: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void,
159
onError?: (error: Error) => void,
160
onCompletion?: () => void
161
): () => void;
162
163
/**
164
* Listen to query changes with options and callback functions
165
* @param options - Snapshot listen options
166
* @param onNext - Function called on each snapshot
167
* @param onError - Optional error handler
168
* @param onCompletion - Optional completion handler
169
* @returns Unsubscribe function to stop listening
170
*/
171
onSnapshot(
172
options: FirebaseFirestoreTypes.SnapshotListenOptions,
173
onNext: (snapshot: FirebaseFirestoreTypes.QuerySnapshot<T>) => void,
174
onError?: (error: Error) => void,
175
onCompletion?: () => void
176
): () => void;
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
import firestore from '@react-native-firebase/firestore';
183
184
const usersQuery = firestore()
185
.collection('users')
186
.where('active', '==', true)
187
.orderBy('createdAt', 'desc');
188
189
// Basic query listener
190
const unsubscribe = usersQuery.onSnapshot(snapshot => {
191
console.log(`Found ${snapshot.size} active users`);
192
193
snapshot.forEach(doc => {
194
console.log(doc.id, '=>', doc.data());
195
});
196
});
197
198
// Listen to document changes
199
const unsubscribeWithChanges = usersQuery.onSnapshot(snapshot => {
200
snapshot.docChanges().forEach(change => {
201
const doc = change.doc;
202
203
switch (change.type) {
204
case 'added':
205
console.log('New user:', doc.data());
206
break;
207
case 'modified':
208
console.log('Updated user:', doc.data());
209
break;
210
case 'removed':
211
console.log('Removed user:', doc.data());
212
break;
213
}
214
});
215
});
216
217
// Include metadata changes to detect local vs server updates
218
const unsubscribeMetadata = usersQuery.onSnapshot(
219
{ includeMetadataChanges: true },
220
snapshot => {
221
if (snapshot.metadata.fromCache) {
222
console.log('Data from cache');
223
} else {
224
console.log('Data from server');
225
}
226
227
snapshot.docChanges({ includeMetadataChanges: true }).forEach(change => {
228
if (change.doc.metadata.hasPendingWrites) {
229
console.log('Document has local changes');
230
}
231
});
232
}
233
);
234
235
// Observer pattern with error handling
236
const unsubscribeObserver = usersQuery.onSnapshot({
237
next: snapshot => {
238
console.log('Query updated:', snapshot.size, 'documents');
239
},
240
error: error => {
241
console.error('Query listener error:', error);
242
}
243
});
244
245
// Stop listening
246
unsubscribe();
247
```
248
249
### Snapshots in Sync
250
251
Listen for when all active listeners are in sync with the server.
252
253
```typescript { .api }
254
/**
255
* Listen for snapshots in sync events with observer pattern
256
* @param observer - Observer object with handlers
257
* @returns Unsubscribe function to stop listening
258
*/
259
onSnapshotsInSync(observer: {
260
next?: () => void;
261
error?: (error: Error) => void;
262
complete?: () => void;
263
}): () => void;
264
265
/**
266
* Listen for snapshots in sync events with callback
267
* @param onSync - Function called when snapshots are in sync
268
* @returns Unsubscribe function to stop listening
269
*/
270
onSnapshotsInSync(onSync: () => void): () => void;
271
```
272
273
**Usage Examples:**
274
275
```typescript
276
import firestore from '@react-native-firebase/firestore';
277
278
// Basic sync listener
279
const unsubscribeSync = firestore().onSnapshotsInSync(() => {
280
console.log('All snapshots are now in sync with the server');
281
});
282
283
// Observer pattern
284
const unsubscribeSyncObserver = firestore().onSnapshotsInSync({
285
next: () => {
286
console.log('Snapshots in sync');
287
},
288
error: error => {
289
console.error('Sync error:', error);
290
}
291
});
292
293
// Stop listening
294
unsubscribeSync();
295
```
296
297
### Managing Multiple Listeners
298
299
Best practices for handling multiple real-time listeners efficiently.
300
301
**Usage Examples:**
302
303
```typescript
304
import firestore from '@react-native-firebase/firestore';
305
306
class UserProfileComponent {
307
private unsubscribers: Array<() => void> = [];
308
309
componentDidMount() {
310
const userId = 'currentUserId';
311
312
// Listen to user profile
313
const userUnsubscribe = firestore()
314
.collection('users')
315
.doc(userId)
316
.onSnapshot(snapshot => {
317
this.setState({ user: snapshot.data() });
318
});
319
320
// Listen to user's posts
321
const postsUnsubscribe = firestore()
322
.collection('posts')
323
.where('authorId', '==', userId)
324
.orderBy('createdAt', 'desc')
325
.onSnapshot(snapshot => {
326
const posts = snapshot.docs.map(doc => ({
327
id: doc.id,
328
...doc.data()
329
}));
330
this.setState({ posts });
331
});
332
333
// Listen to notifications
334
const notificationsUnsubscribe = firestore()
335
.collection('notifications')
336
.where('userId', '==', userId)
337
.where('read', '==', false)
338
.onSnapshot(snapshot => {
339
this.setState({ unreadNotifications: snapshot.size });
340
});
341
342
// Store all unsubscribers
343
this.unsubscribers = [
344
userUnsubscribe,
345
postsUnsubscribe,
346
notificationsUnsubscribe
347
];
348
}
349
350
componentWillUnmount() {
351
// Clean up all listeners
352
this.unsubscribers.forEach(unsubscribe => unsubscribe());
353
this.unsubscribers = [];
354
}
355
}
356
```
357
358
## Types
359
360
```typescript { .api }
361
interface SnapshotListenOptions {
362
/**
363
* Whether to include metadata-only changes
364
* Includes changes to document metadata like pending writes or cache status
365
*/
366
includeMetadataChanges: boolean;
367
}
368
369
interface SnapshotMetadata {
370
/**
371
* True if the snapshot contains the result of local writes not yet committed to the backend
372
*/
373
readonly fromCache: boolean;
374
375
/**
376
* True if the snapshot includes local writes that have not yet been committed to the backend
377
*/
378
readonly hasPendingWrites: boolean;
379
380
/**
381
* Check if two metadata objects are equal
382
*/
383
isEqual(other: FirebaseFirestoreTypes.SnapshotMetadata): boolean;
384
}
385
386
interface DocumentChange<T = FirebaseFirestoreTypes.DocumentData> {
387
/**
388
* The document affected by this change
389
*/
390
readonly doc: FirebaseFirestoreTypes.QueryDocumentSnapshot<T>;
391
392
/**
393
* The new index of the changed document in the result set (-1 if removed)
394
*/
395
readonly newIndex: number;
396
397
/**
398
* The old index of the changed document in the result set (-1 if added)
399
*/
400
readonly oldIndex: number;
401
402
/**
403
* The type of change ('added', 'modified', or 'removed')
404
*/
405
readonly type: FirebaseFirestoreTypes.DocumentChangeType;
406
}
407
408
type DocumentChangeType = 'added' | 'removed' | 'modified';
409
410
/**
411
* Unsubscribe function returned by snapshot listeners
412
*/
413
type Unsubscribe = () => void;
414
```
415
416
## Error Handling
417
418
```typescript { .api }
419
interface FirestoreError extends Error {
420
/**
421
* Error code indicating the type of error
422
*/
423
readonly code: FirebaseFirestoreTypes.FirestoreErrorCode;
424
425
/**
426
* Human-readable error message
427
*/
428
readonly message: string;
429
}
430
431
type FirestoreErrorCode =
432
| 'cancelled'
433
| 'unknown'
434
| 'invalid-argument'
435
| 'deadline-exceeded'
436
| 'not-found'
437
| 'already-exists'
438
| 'permission-denied'
439
| 'resource-exhausted'
440
| 'failed-precondition'
441
| 'aborted'
442
| 'out-of-range'
443
| 'unimplemented'
444
| 'internal'
445
| 'unavailable'
446
| 'data-loss'
447
| 'unauthenticated';
448
```
449
450
**Error Handling Examples:**
451
452
```typescript
453
import firestore from '@react-native-firebase/firestore';
454
455
const userDoc = firestore().collection('users').doc('userId');
456
457
const unsubscribe = userDoc.onSnapshot(
458
snapshot => {
459
// Handle successful snapshot
460
console.log('User data:', snapshot.data());
461
},
462
error => {
463
// Handle errors
464
switch (error.code) {
465
case 'permission-denied':
466
console.error('User does not have permission to access this document');
467
break;
468
case 'unavailable':
469
console.error('Service is currently unavailable');
470
break;
471
case 'unauthenticated':
472
console.error('User is not authenticated');
473
break;
474
default:
475
console.error('Unknown error:', error.message);
476
}
477
}
478
);
479
```