Skip to content

Commit 4ecd58e

Browse files
authored
rxfire: Support collection groups & change detection fixess (#2223)
* rxfire was comparing documents based off ids, this was fine before collection group queries. Here I've switched to `ref.isEqual` like I have done in AngularFire. * When a document is modified, we were only replacing the single doc in the array; this would not always trip change detection in frameworks as `===` would still equal true on the top level. Splicing it creates a new array, which will fail `===` and trip change detection. * Further, this was an issue with the other operations as we were using splice. I'm now slicing to clone the array before modifying. * NIT: Visual Studio Code's settings, which are committed, were not setup to track the workspace's version of Typescript. This tripped me up a little at first.
1 parent c35bc8f commit 4ecd58e

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88

99
// Exclude installed dependencies from searches too
1010
"**/node_modules": true
11-
}
11+
},
12+
"typescript.tsdk": "node_modules/typescript/lib"
1213
}

packages/rxfire/firestore/collection/index.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import { firestore } from 'firebase/app';
1919
import { fromCollectionRef } from '../fromRef';
2020
import { Observable, MonoTypeOperatorFunction } from 'rxjs';
21-
import { map, filter, scan } from 'rxjs/operators';
21+
import { map, filter, scan, distinctUntilChanged } from 'rxjs/operators';
2222
import { snapToData } from '../document';
2323

2424
const ALL_EVENTS: firestore.DocumentChangeType[] = [
@@ -56,6 +56,16 @@ const filterEmpty = filter(
5656
(changes: firestore.DocumentChange[]) => changes.length > 0
5757
);
5858

59+
/**
60+
* Splice arguments on top of a sliced array, to break top-level ===
61+
* this is useful for change-detection
62+
*/
63+
function sliceAndSplice<T>(original: T[], start: number, deleteCount: number, ...args: T[]): T[] {
64+
const returnArray = original.slice();
65+
returnArray.splice(start, deleteCount, ...args);
66+
return returnArray;
67+
}
68+
5969
/**
6070
* Creates a new sorted array from a new change.
6171
* @param combined
@@ -69,35 +79,37 @@ function processIndividualChange(
6979
case 'added':
7080
if (
7181
combined[change.newIndex] &&
72-
combined[change.newIndex].doc.id === change.doc.id
82+
combined[change.newIndex].doc.ref.isEqual(change.doc.ref)
7383
) {
7484
// Skip duplicate emissions. This is rare.
7585
// TODO: Investigate possible bug in SDK.
7686
} else {
77-
combined.splice(change.newIndex, 0, change);
87+
return sliceAndSplice(combined, change.newIndex, 0, change);
7888
}
7989
break;
8090
case 'modified':
8191
if (
8292
combined[change.oldIndex] == null ||
83-
combined[change.oldIndex].doc.id === change.doc.id
93+
combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)
8494
) {
8595
// When an item changes position we first remove it
8696
// and then add it's new position
8797
if (change.oldIndex !== change.newIndex) {
88-
combined.splice(change.oldIndex, 1);
89-
combined.splice(change.newIndex, 0, change);
98+
const copiedArray = combined.slice();
99+
copiedArray.splice(change.oldIndex, 1);
100+
copiedArray.splice(change.newIndex, 0, change);
101+
return copiedArray;
90102
} else {
91-
combined[change.newIndex] = change;
103+
return sliceAndSplice(combined, change.newIndex, 1, change);
92104
}
93105
}
94106
break;
95107
case 'removed':
96108
if (
97109
combined[change.oldIndex] &&
98-
combined[change.oldIndex].doc.id === change.doc.id
110+
combined[change.oldIndex].doc.ref.isEqual(change.doc.ref)
99111
) {
100-
combined.splice(change.oldIndex, 1);
112+
return sliceAndSplice(combined, change.oldIndex, 1);
101113
}
102114
break;
103115
default: // ignore
@@ -167,7 +179,8 @@ export function sortedChanges(
167179
changes: firestore.DocumentChange[]
168180
) => processDocumentChanges(current, changes, events),
169181
[]
170-
)
182+
),
183+
distinctUntilChanged()
171184
);
172185
}
173186

packages/rxfire/test/firestore.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,15 @@ describe('RxFire Firestore', () => {
165165
take(1)
166166
);
167167

168+
let previousData: Array<{}>;
169+
168170
addedChanges.subscribe(data => {
169171
const expectedNames = [
170172
{ name: 'David', type: 'added' },
171173
{ name: 'Shannon', type: 'added' }
172174
];
173175
expect(data).to.eql(expectedNames);
176+
previousData = data;
174177
davidDoc.update({ name: 'David!' });
175178
});
176179

@@ -180,6 +183,7 @@ describe('RxFire Firestore', () => {
180183
{ name: 'Shannon', type: 'added' }
181184
];
182185
expect(data).to.eql(expectedNames);
186+
expect(data === previousData).to.eql(false);
183187
done();
184188
});
185189
});

0 commit comments

Comments
 (0)