NgRx/Effects 不触发 Reducer

Amr*_*uta 2 typescript ngrx-effects angular ngrx-store angular8

我知道问题的标题含糊不清,但不幸的是我不完全知道是什么导致了我的问题,所以请耐心等待。

我刚刚开始处理一个小的 Angular 8 任务,它也使用 NgRx/Store 和 NgRx/Effects。我正在Book使用getBooks函数获取一组对象。我的代码如下(没有把输入端口放在任何地方只是为了使代码部分更短)

book.component.ts

export class BookComponent {

  constructor(private booksService: BooksService, public store: Store<BooksState>) {
    this.books$ = store.pipe(select('books'));
  }

  books$: Observable<BooksState>;
  BooksSubscription: Subscription;
  booksList: Book[];

  ngOnInit() {
    this.BooksSubscription = this.books$
      .pipe( map(x => { this.booksList = x.Books; }) ).subscribe();
    this.store.dispatch(BooksActions.BeginGetBooksAction())
  }
}
Run Code Online (Sandbox Code Playgroud)

书.service.ts

import { Injectable } from '@angular/core';
import booksData from "../../books.json";

@Injectable()
export class BooksService {
  constructor() { }

  getBooks(){
      return booksData;
  }
}
Run Code Online (Sandbox Code Playgroud)

booksDataBook我从json文件导入的类型对象的数组。

书.action.ts

import { createAction, props } from "@ngrx/store";
import { Book } from "./book";

export const GetBooksAction = createAction("GetBooks");
export const BeginGetBooksAction = createAction("BeginGetBooks");
export const SuccessGetBooksAction = createAction( "SuccessGetBooks", props<{ payload: Book[] }>());

Run Code Online (Sandbox Code Playgroud)

book.reducer.ts

import { Action, createReducer, on } from "@ngrx/store";
import * as BooksActions from "./book.action";
import { Book } from "./book";

export interface BooksState {
  Books: Book[];
}

const initialState: BooksState = {
    Books: []
};

const booksReducer = createReducer(
    initialState,
    on(BooksActions.GetBooksAction, state => state))
    on(BooksActions.SuccessGetBooksAction, (state: BooksState, { payload }) => {
      console.log(payload)
      return { ...state, Books: payload };
    });

  export function reducer(state: BooksState | undefined, action: Action) {
    return booksReducer(state, action);
  }
Run Code Online (Sandbox Code Playgroud)

book.effect.ts

现在这就是我迷失的地方。我在 NgRx 文档上找到了一个参考代码,我根据它编写了代码,但它不起作用

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs';
import { map, mergeMap} from 'rxjs/operators';
import * as BooksActions from './book.action';
import { Book } from './book';
import { BooksService } from './book.service';


@Injectable()
export class BooksEffects {
    constructor(private action$: Actions, private booksService: BooksService) { }

     GetBooks$: Observable<Action> = createEffect(() =>
        this.action$.pipe(
            ofType(BooksActions.BeginGetBooksAction),
            mergeMap(
                () => this.booksService.getBooks().pipe(
                    map((booksData: Book[]) => {
                        return BooksActions.SuccessGetBooksAction({ payload: booksData });
                    }))
            )
        )
    )
  );
}
Run Code Online (Sandbox Code Playgroud)

我在编译过程中遇到以下错误。错误在我有的地方pipe functions

error TS2322: Type 'Observable<unknown>' is not assignable to type 'Observable<Action> | ((...args: any[]) 
=> Observable<Action>)'.
      Type 'Observable<unknown>' is not assignable to type 'Observable<Action>'.
        Property 'type' is missing in type '{}' but required in type 'Action'.
Run Code Online (Sandbox Code Playgroud)

error TS2339: Property 'pipe' does not exist on type '{ "isbn": string; "title": string; "subtitle": string; "author": string; "published": string; "publisher": string; "pages": number; "description": string; "website": string; }[]'.

另外,我已经加入StoreModule.forRoot({ books: reducer})EffectsModule.forRoot([BooksEffects])imports并添加BookServiceProvidersapp.module.ts

pas*_*etz 5

您的代码中有两个错误。

MergeMap 使用不当

this.booksService.getBooks()似乎没有返回Observable. 在这种情况下,您要使用map代替mergeMap

GetBooks$: Observable<Action> = createEffect(() =>
   this.action$.pipe(
      ofType(BooksActions.BeginGetBooksAction),
      map(() => this.booksService.getBooks()),
      map(payload => BooksActions.SuccessGetBooksAction({ payload })                
   )
);

Run Code Online (Sandbox Code Playgroud)

详细说明,mergeMap当您想从一个 Observable 切换到另一个 Observable(类似于switchMap)时使用。回调预计会返回一个 Observable,然后mergeMap可以订阅。for 的回调map预计会返回一个值并将该值包装到一个新的 Observable 中。由于您getBooks()没有返回 Observable,mergeMap无法订阅它,TypeScript 知道这一点并抛出所述错误。或者,您也可以getBooks()改为返回一个 Observable(如果您以后想从 HTTP 请求中获取这些,这将返回一个 Observable,这将是一种可行的方法。

减速器中的括号错误

此外,经过进一步检查,您的效果和减速器实际上正在运行。但是您在减速器中的括号有一个小错误。看这个:

const booksReducer = createReducer(
    initialState,
    on(BooksActions.GetBooksAction, state => state))  // <---- This ")" is closing your "createReducer" function!
    on(BooksActions.SuccessGetBooksAction, (state: BooksState, { payload }) => {
      console.log(payload)
      return { ...state, Books: payload };
    });
Run Code Online (Sandbox Code Playgroud)

所以如果你格式化它以反映它在做什么,你写道:

const booksReducer = createReducer(
    initialState,
    on(BooksActions.GetBooksAction, state => state)
)

// You "on" function is actually outside "createReducer" effectively NEVER being triggered (but this is syntactically correct so a very tricky error)
on(BooksActions.SuccessGetBooksAction, (state: BooksState, { payload }) => {
   console.log(payload)
   return { ...state, Books: payload };
});
Run Code Online (Sandbox Code Playgroud)

所以你只需要修复那个:

const booksReducer = createReducer(
    initialState,
    on(BooksActions.GetBooksAction, state => state),
    on(BooksActions.SuccessGetBooksAction, (state: any, { payload }) => {
      console.log('Reducer is running, value is:', payload)
      return { ...state, Books: payload };
    })
);
Run Code Online (Sandbox Code Playgroud)

这是一个有效的Stackblitz。查看控制台以查看正在触发的所有内容。