arraysangularngrxstate-management

Application state breaks when a blank array of data is returned from service


Problem

Using ngrx in an Angular 18 application I have set up an effect with the following code (I apologize for the odd naming conventions as I had to obfuscate the data):

loadAnalytics$ : Observable<AnalyticsState> = createEffect(() =>
        this.actions$.pipe(
            ofType(getAnalytics),
            withLatestFrom(this.store.select(selectAllAnalytics)),
            filter(([action, latest]) => 
                !latest.filter(p => p.pi === action.pi).length ? true : false
            ),
            switchMap(([action, latest]) => this.AnalyticsService.get(action.pi).pipe(
                map(analytics => analytics as Analytics[]),
                switchMap(
                    (analytics) =>
                    of(getAnyalticsSuccess({analytics}))
                )
            ))
        )
    )

The data is returned from the service two ways:

  1. if the value of pi returns data then an array of {pi: ''} is returned
  2. if no value of pi returns data then [] is returned

At point 2 this is where the state breaks and gives me the following error in the console:

TypeError: provider_analytics is not iterable

The stack trace points me to the reducer function, but I'm not seeing the issue. The spread operator should handle an empty array.

The state is modified here in the following reducer:


export const AnalyticsReducter = createReducer(
    initialState,
    on(getAnalyticsSuccess, (state, {analytics}) => [...state, ...analytics])
)

the data is fetched in the following selector

export const selectAnalytics = (pi: String) => createSelector(selectAnalytics, (analytics) => 
    analytics.filter(p => p.pi == pi) || []
)

Attempted Solutions

I figured if I put a return value of analytics.filter(p => p.pi == pi) || [] and returning the empty array from the call would work, but that did nothing


Solution

  • I think its because of your filter, we can do a filter only of iterable if analytics is not an iterable it's wont work. maybe do the following: (analytics || []).filter(p => p.pi == pi)