• ngRx 官方示例分析


    @ngrx/effect 

    前面我们提到,在 Book 的 reducer 中,并没有 Search 这个 Action 的处理,由于它需要发出一个异步的请求,等到请求返回前端,我们需要根据返回的结果来操作 store。所以,真正操作 store 的应该是 Search_Complete 这个 Action。我们在 recducer 已经看到了。

    对于 Search 来说,我们需要见到这个 Action 就发出一个异步的请求,等到异步处理完毕,根据返回的结果,构造一个 Search_Complete 来将处理的结果派发给 store 进行处理。

    这个解耦是通过 @ngrx/effect 来处理的。

    @ngrx/effect 提供了装饰器 @Effect 和 Actions 来帮助我们检查 store 派发出来的 Action,将特定类型的 Action 过滤出来进行处理。监听特定的 Action, 当发现特定的 Action 发出之后,自动执行某些操作,然后将处理的结果重新发送回 store 中。

    Book 搜索处理 

    以 Book 为例,我们需要监控 Search 这个 Action, 见到这个 Action 就发出异步的请求,然后接收请求返回的数据,如果在接收完成之前,又遇到了下一个请求,那么,直接结束上一个请求的返回数据。如何找到下一个请求呢?使用 skip 来获取下一个 Search Action。

    源码

    /src/effects/book.ts

    import 'rxjs/add/operator/catch';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/switchMap';
    import 'rxjs/add/operator/debounceTime';
    import 'rxjs/add/operator/skip';
    import 'rxjs/add/operator/takeUntil';
    import { Injectable } from '@angular/core';
    import { Effect, Actions, toPayload } from '@ngrx/effects';
    import { Action } from '@ngrx/store';
    import { Observable } from 'rxjs/Observable';
    import { empty } from 'rxjs/observable/empty';
    import { of } from 'rxjs/observable/of';
    
    import { GoogleBooksService } from '../services/google-books';
    import * as book from '../actions/book';
    
    
    /**
     * Effects offer a way to isolate and easily test side-effects within your
     * application.
     * The `toPayload` helper function returns just
     * the payload of the currently dispatched action, useful in
     * instances where the current state is not necessary.
     *
     * Documentation on `toPayload` can be found here:
     * https://github.com/ngrx/effects/blob/master/docs/api.md#topayload
     *
     * If you are unfamiliar with the operators being used in these examples, please
     * check out the sources below:
     *
     * Official Docs: http://reactivex.io/rxjs/manual/overview.html#categories-of-operators
     * RxJS 5 Operators By Example: https://gist.github.com/btroncone/d6cf141d6f2c00dc6b35
     */
    
    @Injectable()
    export class BookEffects {
    
      @Effect()
      search$: Observable<Action> = this.actions$
        .ofType(book.ActionTypes.SEARCH)
        .debounceTime(300)
        .map(toPayload)
        .switchMap(query => {
          if (query === '') {
            return empty();
          }
    
          const nextSearch$ = this.actions$.ofType(book.ActionTypes.SEARCH).skip(1);
    
          return this.googleBooks.searchBooks(query)
            .takeUntil(nextSearch$)
            .map(books => new book.SearchCompleteAction(books))
            .catch(() => of(new book.SearchCompleteAction([])));
        });
    
        constructor(private actions$: Actions, private googleBooks: GoogleBooksService) { }
    }

     collection.ts

    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/operator/startWith';
    import 'rxjs/add/operator/switchMap';
    import 'rxjs/add/operator/mergeMap';
    import 'rxjs/add/operator/toArray';
    import { Injectable } from '@angular/core';
    import { Action } from '@ngrx/store';
    import { Effect, Actions } from '@ngrx/effects';
    import { Database } from '@ngrx/db';
    import { Observable } from 'rxjs/Observable';
    import { defer } from 'rxjs/observable/defer';
    import { of } from 'rxjs/observable/of';
    
    import * as collection from '../actions/collection';
    import { Book } from '../models/book';
    
    
    @Injectable()
    export class CollectionEffects {
    
      /**
       * This effect does not yield any actions back to the store. Set
       * `dispatch` to false to hint to @ngrx/effects that it should
       * ignore any elements of this effect stream.
       *
       * The `defer` observable accepts an observable factory function
       * that is called when the observable is subscribed to.
       * Wrapping the database open call in `defer` makes
       * effect easier to test.
       */
      @Effect({ dispatch: false })
      openDB$: Observable<any> = defer(() => {
        return this.db.open('books_app');
      });
    
      /**
       * This effect makes use of the `startWith` operator to trigger
       * the effect immediately on startup.
       */
      @Effect()
      loadCollection$: Observable<Action> = this.actions$
        .ofType(collection.ActionTypes.LOAD)
        .startWith(new collection.LoadAction())
        .switchMap(() =>
          this.db.query('books')
            .toArray()
            .map((books: Book[]) => new collection.LoadSuccessAction(books))
            .catch(error => of(new collection.LoadFailAction(error)))
        );
    
      @Effect()
      addBookToCollection$: Observable<Action> = this.actions$
        .ofType(collection.ActionTypes.ADD_BOOK)
        .map((action: collection.AddBookAction) => action.payload)
        .mergeMap(book =>
          this.db.insert('books', [ book ])
            .map(() => new collection.AddBookSuccessAction(book))
            .catch(() => of(new collection.AddBookFailAction(book)))
        );
    
    
      @Effect()
      removeBookFromCollection$: Observable<Action> = this.actions$
        .ofType(collection.ActionTypes.REMOVE_BOOK)
        .map((action: collection.RemoveBookAction) => action.payload)
        .mergeMap(book =>
          this.db.executeWrite('books', 'delete', [ book.id ])
            .map(() => new collection.RemoveBookSuccessAction(book))
            .catch(() => of(new collection.RemoveBookFailAction(book)))
        );
    
        constructor(private actions$: Actions, private db: Database) { }
    }

      

    See also:

    @ngrx/effects

    skip

    takeUntil

    What is the purpose of ngrx effects library?

  • 相关阅读:
    windchill系统安装大概步骤
    Javase、Javaee、Javame的区别
    Cocoa Touch事件处理流程--响应者链
    iOS KVC & KVO
    GET异步 请求图片步骤
    iOS7——图像资源Images Assets
    IOS编程教程(八):在你的应用程序添加启动画面
    Objective C内存管理之理解autorelease------面试题
    runtime 运行时机制 完全解读
    图片的异步下载
  • 原文地址:https://www.cnblogs.com/haogj/p/6619043.html
Copyright © 2020-2023  润新知