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.
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.