Throwing an error TypeError: Cannot add property 0, object is not extensible

Viewed 297

Please see the code below for reference.

export const listenToHotels = (hotelIds: string[]): Observable < Hotel[] > => {
    return new Observable < Hotel[] > ((observer) => {
        const hotels: any = [];

        hotelIds.forEach((hotelId) => {
            let roomingList: any;
            return FirestoreCollectionReference.Hotels()
                .doc(hotelId)
                .onSnapshot(
                    (doc) => {
                        roomingList = {
                            hotelId: doc.id,
                            ...doc.data()
                        }
                        as Hotel;
                        console.log(`roomingList`, roomingList);
                        hotels.push({
                            ...roomingList
                        });

                    },
                    (error) => observer.error(error)
                );
        });
        //Check for error handling
        observer.next(hotels);
        console.log('hotels', hotels);

    });
};

As you can see I am trying to run a forEach on a hotelId Array and in that firestore listener is being executed. Now I want to save the response and push that into hotels array but it gives me an error object not extensible error. The thing is observer and console.log('hotels',hotels) run first because of promise being executed at later stage.

Please let me know how can I resolve this issue.

3 Answers

Check out forkJoin. It takes an array of Observables and subscribes to them at the same time.

This example is written in Angular but the operators should be identical as long as you're using a current version of rxjs.

  • I'm starting with an array of User objects (users.mock.ts)
  • Each Object is then map to a single Observable (mapCalls)
  • I now have an array of Observables that will make HTTP calls. These calls will not be made until subscribed to
  • forkJoin is then added as a wrapper to the array
  • You subscribe to that forkJoin. All of the objects will make the call.
  • When the calls have completed, the logic inside of subscribe() will be run

So in your case:

  • Map the Hotel Ids to a variable calls. Each item is the Observable logic you posted
  • You would then run forkJoin(calls).subscribe() to make all the calls
export const listenToHotels = (hotelIds: string[]): Observable<Hotel[]> => {
    const hotelsObservable = hotelIds.map((hotelId) => {
        let roomingList: any;
        return new Observable<Hotel>((observerInside) => {
            FirestoreCollectionReference.Hotels()
                .doc(hotelId)
                .onSnapshot(
                    (doc) => {
                        roomingList = { hotelId: doc.id, ...doc.data() } as Hotel;
                        observerInside.next(roomingList);
                    },
                    (error) => observerInside.error(error)
                );
        });
    });
    const combinedObservable = combineLatest(hotelsObservable);
    return combinedObservable;
    //Check for error handling
};

The issue was how I was handling the chained observables(Execution of promise is delayed than a normal code because they will be put in micro-queue first). They need to be handled using zip or combineLatest but latter is much better in this use case as we need the latest values for the observables.

Related