简体   繁体   English

ngrx /特效库的目的是什么?

[英]What is the purpose of ngrx/effects library?

I have failed to find any usefull information about this library or what is the purpose of it. 我没有找到有关此库的任何有用信息或它的目的是什么。 It seems like ngrx/effects explain this library to developers who already know this concept and gives a bigginer example on how to code. 似乎ngrx / effects向已经了解这个概念的开发人员解释了这个库,并提供了一个关于如何编码的更大的例子。

My questions: 我的问题:

  1. What are sources of actions? 什么是行动的来源?
  2. What is the purpose of ngrx/effects library;what is the downside of only using ngrx/store? ngrx / effects库的目的是什么;仅使用ngrx / store的缺点是什么?
  3. When it is recommended to be used? 什么时候建议使用?
  4. Does it support angular rc 5+? 它是否支持角度rc 5+? How do we configure it in rc 5+? 我们如何在rc 5+中配置它?

Thanks! 谢谢!

The topic is too wide. 话题太广了。 It will be like a tutorial. 它就像一个教程。 I will give it a try anyway. 无论如何我会尝试一下。 In a normal case, you will have an action, reducer and a store. 在正常情况下,您将拥有一个动作,减速器和一个商店。 Actions are dispatched by the store, which is subscribed to by the reducer. 商店将调度操作,由reducer订阅。 Then the reducer acts on the action, and forms a new state. 然后,reducer对动作起作用,并形成一个新状态。 In examples, all states are at the frontend, but in a real app, it needs to call backend DB or MQ, etc, these calls have side effects. 在示例中,所有状态都在前端,但在实际应用中,它需要调用后端DB或MQ等,这些调用具有副作用。 The framework used to factor out these effects into a common place. 用于将这些影响分解为一个共同点的框架。

Let's say you save a Person Record to your database, action: Action = {type: SAVE_PERSON, payload: person} . 假设您将人员记录保存到数据库, action: Action = {type: SAVE_PERSON, payload: person} Normally your component won't directly call this.store.dispatch( {type: SAVE_PERSON, payload: person} ) to have the reducer call the HTTP service, instead it will call this.personService.save(person).subscribe( res => this.store.dispatch({type: SAVE_PERSON_OK, payload: res.json}) ) . 通常,您的组件不会直接调用this.store.dispatch( {type: SAVE_PERSON, payload: person} )让reducer调用HTTP服务,而是调用this.personService.save(person).subscribe( res => this.store.dispatch({type: SAVE_PERSON_OK, payload: res.json}) ) The component logic will get more complicated when adding real life error handling. 添加实际生活错误处理时,组件逻辑将变得更加复杂。 To avoid this, it will be nice to just call this.store.dispatch( {type: SAVE_PERSON, payload: person} ) from your component. 为了避免这种情况,最好从组件中调用this.store.dispatch( {type: SAVE_PERSON, payload: person} )

That is what the effects library is for. 这就是效果库的用途。 It acts like a JEE servlet filter in front of reducer. 它在减速器前面就像一个JEE servlet过滤器。 It matches the ACTION type (filter can match urls in java world) and then acts on it, and finally returns a different action, or no action, or multiple actions. 它匹配ACTION类型(过滤器可以匹配java世界中的url)然后对其进行操作,最后返回不同的操作,或者不执行任何操作或多个操作。 Then the reducer responds to the output actions of effects. 然后减速器响应效果的输出动作。

To continue the previous example, with the effects library: 要继续上一个示例,请使用效果库:

@Effects() savePerson$ = this.stateUpdates$.whenAction(SAVE_PERSON)
   .map<Person>(toPayload)
   .switchMap( person => this.personService.save(person) )
   .map( res => {type: SAVE_PERSON_OK, payload: res.json} )
   .catch( e => {type: SAVE_PERSON_ERR, payload: err} )

The weave logic is centralised into all Effects and Reducers classes. 编织逻辑集中到所有Effects和Reducers类中。 It can easily grow more complicated, and at the same time this design makes other parts much simpler and more re-usable. 它可以轻松地变得更加复杂,同时这种设计使其他部件更加简单和可重复使用。

For example if the UI has auto saving plus manually saving, to avoid unnecessary saves, UI auto save part can just be triggered by timer and manual part can be triggered by user click. 例如,如果UI具有自动保存加手动保存,为了避免不必要的保存,UI自动保存部分只能由计时器触发,手动部分可以通过用户点击触发。 Both would dispatch a SAVE_CLIENT action. 两者都会调度SAVE_CLIENT操作。 The effects interceptor can be: 效果拦截器可以是:

@Effects() savePerson$ = this.stateUpdates$.whenAction(SAVE_PERSON)
   .debounce(300).map<Person>(toPayload)
   .distinctUntilChanged(...)
   .switchMap( see above )
   // at least 300 milliseconds and changed to make a save, otherwise no save

The call 电话

...switchMap( person => this.personService.save(person) )
   .map( res => {type: SAVE_PERSON_OK, payload: res.json} )
   .catch( e => Observable.of( {type: SAVE_PERSON_ERR, payload: err}) )

only works once if there is an error. 只有在出现错误时才能使用一次。 The stream is dead after an error is thrown because the catch tries on outer stream. 抛出错误后流已经死了因为catch尝试外部流。 The call should be 电话应该是

...switchMap( person => this.personService.save(person)
   .map( res => {type: SAVE_PERSON_OK, payload: res.json} )
   .catch( e => Observable.of( {type: SAVE_PERSON_ERR, payload: err}) ) )

Or another way: change all ServiceClass services methods to return ServiceResponse which contains error code, error message and wrapped response object from server side, ie 或者另一种方式:更改所有ServiceClass服务方法以返回ServiceResponse,其中包含来自服务器端的错误代码,错误消息和包装的响应对象,即

export class ServiceResult {    
    error:     string;    
    data:      any;

    hasError(): boolean {
       return error != undefined && error != null;    }

    static ok(data: any): ServiceResult {
       let ret = new ServiceResult();
       ret.data = data;
       return ret;    
    }

    static err(info: any): ServiceResult {
       let ret = new ServiceResult();
       ret.error = JSON.stringify(info);
       return ret;    
   } 
}

@Injectable()
export class PersonService {
   constructor(private http: Http) {}
   savePerson(p: Person): Observable<ServiceResult> {
       return http.post(url, JSON.stringify(p)).map(ServiceResult.ok);
              .catch( ServiceResult.err ); 
   }
}

@Injectable()
export class PersonEffects {
  constructor(
    private update$: StateUpdates<AppState>,
    private personActions: PersonActions,
    private svc: PersonService
  ){
  }

@Effects() savePerson$ = this.stateUpdates$.whenAction(PersonActions.SAVE_PERSON)
   .map<Person>(toPayload)
   .switchMap( person => this.personService.save(person) )
   .map( res => {
       if (res.hasError()) {
           return personActions.saveErrAction(res.error);
       } else {
           return personActions.saveOkAction(res.data);
       }
   });

@Injectable()
export class PersonActions {
    static SAVE_OK_ACTION = "Save OK";
    saveOkAction(p: Person): Action {
       return {type: PersonActions.SAVE_OK_ACTION,
               payload: p};
    }

    ... ...
}

One correction to my previous comment: Effect-Class and Reducer-Class, if you have both Effect-class and Reducer-class react to the same action type, Reducer-class will react first, and then Effect-class. 对我之前评论的一个修正:Effect-Class和Reducer-Class,如果你同时对Effect-class和Reducer-class做出反应,则Reducer-class将首先做出反应,然后是Effect-class。 Here is an example: One component has a button, once clicked, called: this.store.dispatch(this.clientActions.effectChain(1)); 下面是一个例子:一个组件有一个按钮,一旦被点击,称为: this.store.dispatch(this.clientActions.effectChain(1)); which will be handled by effectChainReducer , and then ClientEffects.chainEffects$ , which increases the payload from 1 to 2; 这将由effectChainReducer处理,然后由effectChainReducer ClientEffects.chainEffects$ ,它将有效负载从1增加到2; wait for 500 ms to emit another action: this.clientActions.effectChain(2) , after handled by effectChainReducer with payload=2 and then ClientEffects.chainEffects$ , which increases to 3 from 2, emit this.clientActions.effectChain(3) , ..., until it is greater than 10, ClientEffects.chainEffects$ emits this.clientActions.endEffectChain() , which changes the store state to 1000 via effectChainReducer , finally stops here. 等待500毫秒发出另一个动作: this.clientActions.effectChain(2) ,在由effectChainReducer处理,有效载荷= 2,然后ClientEffects.chainEffects$ ,从2增加到3,发出this.clientActions.effectChain(3) , ...,直到它大于10, ClientEffects.chainEffects$发出this.clientActions.endEffectChain() ,它通过effectChainReducer将存储状态更改为1000,最后在此处停止。

    export interface AppState {
      ... ...

      chainLevel:     number;
    }

    // In NgModule decorator
    @NgModule({
       imports: [...,
            StoreModule.provideStore({
                ... ...
                chainLevel: effectChainReducer
              }, ...],
       ...
       providers: [... runEffects(ClientEffects) ],
       ...
    })
    export class AppModule {}


    export class ClientActions {
      ... ...
      static EFFECT_CHAIN = "Chain Effect";
      effectChain(idx: number): Action {
        return {
              type: ClientActions.EFFECT_CHAIN,
              payload: idx
        };
      }

      static END_EFFECT_CHAIN = "End Chain Effect";
      endEffectChain(): Action {
        return {
          type: ClientActions.END_EFFECT_CHAIN,
        };
      }

  static RESET_EFFECT_CHAIN = "Reset Chain Effect";
  resetEffectChain(idx: number = 0): Action {
    return {
      type: ClientActions.RESET_EFFECT_CHAIN,
      payload: idx
    };

    }

    export class ClientEffects {
      ... ...
      @Effect()
      chainEffects$ = this.update$.whenAction(ClientActions.EFFECT_CHAIN)
        .map<number>(toPayload)
        .map(l => {
          console.log(`effect chain are at level: ${l}`)
          return l + 1;
        })
        .delay(500)
        .map(l => {
          if (l > 10) {
             return this.clientActions.endEffectChain();
          } else {
             return this.clientActions.effectChain(l);
          }
        });
    }

    // client-reducer.ts file
    export const effectChainReducer = (state: any = 0, {type, payload}) => {
      switch (type) {
        case ClientActions.EFFECT_CHAIN:
          console.log("reducer chain are at level: " + payload);
          return payload;
        case ClientActions.RESET_EFFECT_CHAIN:
          console.log("reset chain level to: " + payload);
          return payload;
        case ClientActions.END_EFFECT_CHAIN:
          return 1000;
        default:
          return state;
      }
    }

If you run the above code, the output should look like: 如果运行上面的代码,输出应如下所示:

client-reducer.ts:51 reducer chain are at level: 1 client-reducer.ts:51减速机链处于等级:1
client-effects.ts:72 effect chain are at level: 1 client-effects.ts:72效果链处于等级:1
client-reducer.ts:51 reducer chain are at level: 2 client-reducer.ts:51减速机链处于等级:2
client-effects.ts:72 effect chain are at level: 2 client-effects.ts:72效果链处于等级:2
client-reducer.ts:51 reducer chain are at level: 3 client-reducer.ts:51减速机链处于等级:3
client-effects.ts:72 effect chain are at level: 3 client-effects.ts:72效果链处于等级:3
... ... ......
client-reducer.ts:51 reducer chain are at level: 10 client-reducer.ts:51减速机链处于等级:10
client-effects.ts:72 effect chain are at level: 10 client-effects.ts:72效果链处于等级:10

It indicates reducer runs first before effects, Effect-Class is a post-interceptor, not pre-interceptor. 它表示reducer在效果之前首先运行,Effect-Class是一个后拦截器,而不是预拦截器。 See flow diagram: 见流程图: 在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM