Search code examples
javascriptangularrxjsngrxngrx-effects

Unable to see store state changes displayed in component


I am just setting up a simple component to work with ngrx, and to use the async pipe in my template to react to changes in values.

But I am hitting an issue where although I see data is being successfully fetched from my effect (I see successful network request) and I see my reducer function invoked (if I add a breakpoint in the code) I don't see any change in what is rendered where I expect to see some elements displayed listing some record ids.

Here is what I have thrown together so far...

actions:

import { createAction, props } from '@ngrx/store';

export const fetchSharedDashboards = createAction('[Dashboard] Fetch Shared Dashboards');

export const fetchSharedDashboardSuccess = createAction(
  '[Dashboards API] Fetch Shared Dashboards Success',
  props<{ data: any }>()
);

reducers:

import { createReducer, on, Action } from '@ngrx/store';
import * as DashboardActions from './dashboard.actions';
import { SharedDashboardItem } from '@al/dashboards';

export const appFeatureKey = 'dashboards';

export interface DashboardState {
  sharedDashboards: SharedDashboardItem[];
}

export const initialState: DashboardState = {
  sharedDashboards: null
};

export const appReducer = createReducer(
  initialState,
  on(DashboardActions.fetchSharedDashboardSuccess, (state: DashboardState, action) => ({
    ...state,
    sharedDashboards: action.data,
  }))

);

export function reducer(state: DashboardState | undefined, action: Action) {
  return appReducer(state, action);
}

effects:

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY, from } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import * as AppActions from './dashboard.actions';
import { DashboardsClient } from '@al/dashboards';

@Injectable()
export class DashboardEffects {

  onFetchSharedDashboards$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(AppActions.fetchSharedDashboards),
      switchMap(() => from(DashboardsClient.listDashboardGroups('2'))
        .pipe(
          map(data => {
            console.log(data) // this logs out an object with an array of populated dashboard_items as expected
            return AppActions.fetchSharedDashboardSuccess({data: data.dashboard_items}
          )}),
          catchError(err => {
            console.error('error')
            return EMPTY;
          })
        ))
      )
      });

  constructor(
    private actions$: Actions
  ) {}
}

component TS:

import { SharedDashboardItem } from "@al/dashboards";
import { Component, OnInit } from "@angular/core";
import { Store } from "@ngrx/store";
import { Observable } from "rxjs";
import { fetchSharedDashboards } from "./dashboard.actions";
import { DashboardState } from "./dashboard.reducer";

@Component({
  selector: 'app-dashboards-list',
  templateUrl: './dashboards-list.component.html'
})

export class DashboardsListComponent implements OnInit {

  sharedDashboards$: Observable<SharedDashboardItem[]> = this.store.select(state => state.sharedDashboards);
 
  constructor(private store: Store<DashboardState>) {}
 
  ngOnInit() {
    this.store.dispatch(fetchSharedDashboards());
    this.sharedDashboards$.subscribe(val => {
      console.log(val) // only ever see the initial undefined printed out to the console here
    })
  }

}

component html:

<div *ngFor="let shared of sharedDashboards$ | async">
  <div>{{shared.id}}</div>
</div>

I added a subscribe to the sharedDashboards$ observable in my component's ngOnit to console log values being emitted, but I only ever see the undefined value assigned in my initial state of my reducer when created.

enter image description here

I am clearly missing something fundamental here, can someone point out my problem here please?

SOLVED

I am using a feature key 'dashboards' to setup the store in my feature module configuration, e.g

StoreModule.forRoot({ [fromApp.appFeatureKey]: fromApp.reducer })

The devtools helped me out as suggested as I could see the extra key which I should have been referencing like so in my component:

sharedDashboards$: Observable<SharedDashboardItem[]> = this.store.select((state: any) => state.dashboards.sharedDashboards);

All working now!


Solution

  • sharedDashboards$: Observable<SharedDashboardItem[]> = this.store.select(state => state.sharedDashboards);
    

    This code probably doesn't read the correct slice. You should first select the state before selecting sharedDashboards.

    It's hard to say what this needs to be in your case because we don't see how the store is configured, or how the state tree is structured.

    You can use the devtools or log the whole state to see which paths needs to be traversed in order to select sharedDashboards.