I have an RxJS Observable that emits a series of changes to an underlying data structure—specifically, snapshotChanges() from an AngularFirestoreCollection.
- I'm currently mapping this to an array of plain JavaScript objects for consumption by my app.
- This array is not protected in any way, and consuming code could accidentally modify this structure.
- The entire array is rebuilt whenever the underlying data source emits, even if only one (or sometimes no) item in the array has actually changed.
- Because of this, all references change each time, making change detection harder than it needs to be—and really slowing down my app.
What I want to do instead is use Immer to maintain an immutable structure, such that unchanged data is structurally shared with the “new” array.
What I can't work out is how to pipe() off the snapshotChanges() observable such that the pipe gets access to the previously emitted immutable data (or a first-time default) in addition to the latest snapshotChanges() output.
In code, what I basically already have is this:
const docToObject = (doc) => { /* change document to fresh plain object every time */ };
const mappedData$ = snapshotChanges().pipe(
map(changes => changes.map(change => docToObject(change.payload.doc)),
tap(array => console.log('mutable array:', array)),
);
and I'm essentially looking for something like this, where I don't know what XXX(...) should be:
const newImmutableObject = (changes, old) => {
// new immutable structure from old one + changes, structurally sharing as much as
// possible
};
const mappedData$ = snapshotChanges().pipe(
// ==================================================================================
XXX(...), // missing ingredient to combine snapshotChanges and previously emitted
// value, or default to []
// ==================================================================================
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);
I feel like the expand operator is close to what I need, but it seems to only pass the previously emitted value in on subsequent runs, whereas I also need the newly emitted snapshotChanges.
Given an RxJS Observable pipe, how can I operate on this Observable's emissions while also having access to the pipe's previous emission?