Search code examples
angularngrx

ngrx 9 doesn't give me data


I started learning ngrx yesterday. So, for some practice I decided to rewrite my component using it. It used to work nice without ngrx. My showcase.component.ts:

@Component({
  selector: 'app-showcase',
  templateUrl: './showcase.component.html',
  styleUrls: ['./showcase.component.css']
})
export class ShowcaseComponent implements OnInit {
  books$ = this._store.pipe(select(selectShowcaseList));
  constructor(private _store: Store<IAppState>) {    
  }    
  ngOnInit(): void {
    this._store.dispatch(getBooks());
  }
}

showcase.effects.ts (I guess trouble is here):

@Injectable()
export class ShowcaseEffects {
    getBooks$ = createEffect(() => this.actions$.pipe(
        ofType('[Showcase] getBooks'),
        mergeMap(() => this.booksService.getBooks()
          .pipe(
            map(books => ({ type: '[Showcase API] BooksLoaded Success', payload: books })),
            catchError(() => EMPTY)
          ))
        )
      );


    constructor(
        private actions$: Actions,
        private booksService: BookService,
        private _store: Store<IAppState>
    ) { }
}

showcase.reducer.ts:

export const showcaseReducer = createReducer(
    initialState,
    on(showcaseActions.getBooksSuccess, (state, {books}) => ({ ...state, books: books})),
)

showcase.actions.ts:

export const getBooks = createAction(
    '[Showcase] getBooks'
)

export const getBooksSuccess = createAction(
    '[Showcase API] BooksLoaded Success',
    props<{books: Observable<Book[]>}>()
);

showcase.state.ts:

export interface IShowCaseState {
    books: Observable<Book[]>;
}

export const initialState: IShowCaseState = {
    books: null
}

showcase.selectors.ts:

export const selectShowcase = (state: IAppState) => state.showcase;

export const selectShowcaseList = createSelector(
    selectShowcase,
    (state: IShowCaseState) => state.books
);

my states are:

export interface IAppState {
    showcase: IShowCaseState;
    test: ITestState;
}

export interface IShowCaseState {
    books: Book[];
}

export const initialState: IShowCaseState = {
    books: null
}

app.module:

@NgModule({
  declarations: [
    AppComponent,
    AuthenticationComponent,
    ShowcaseComponent,
    CartComponent,
    AccountComponent,
    CreateBookComponent,
    TestComponent //ttt
  ],
  imports: [
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireDatabaseModule,
    BrowserModule,
    AppRoutingModule,
    //запихнуть в отдельный модуль
    StoreModule.forRoot({ books: fromShowcase.showcaseReducer, test: fromTest.testReducer }),
    EffectsModule.forRoot([ShowcaseEffects]),
    StoreDevtoolsModule.instrument({
      maxAge: 10
    }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Console tells me that variable books$ is undefined. I haven't found nice example with the new NGRX 9.X syntaxes for my purposes. If you have any NGRX 9.X app examples where effects with data fetching services are used send me a link please.


Solution

  • You're sending the wrong data on the payload. Your reducer is expecting an object with a books attribute, but instead, it's getting an array of books. You can fix it by changing a little bit your effect:

    import * as fromActions from '...showcase.actions'
    
    ...
    
    getBooks$ = createEffect(() => this.actions$.pipe(
      ofType(fromActions.getBooks),
    
      // instead of returning an empty operator in catchError, let's return an empty array
      switchMap(() => this.booksService.getBooks().pipe(catchError(() => of([])))),
    
      // the main problem in your code was this "payload: books"; use, instead, "payload: {books}"
      map(books => fromActions.getBooksSuccess({books})),
    ));
    

    And let's fix your getBoosSuccess action

    export const getBooksSuccess = createAction(
        '[Showcase API] BooksLoaded Success',
        props<{books: Book[]}>()
    );
    

    Also, fix your state:

    export interface IShowCaseState {
        books: Book[];
    }
    
    export const initialState: IShowCaseState = {
        books: []
    }
    

    I've put something together on stackblitz.