Angular 2 更新全局状态作为更新某个状态的副作用 [英] Angular 2 update global state as a side effect of updating a certain state

查看:19
本文介绍了Angular 2 更新全局状态作为更新某个状态的副作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现设置全局状态,同时还从 api 请求数据并将其存储在状态中,但存储在全局状态以外的其他位置.

我称之为效果(models.effects.ts):

@Effect() models$: Observable= this.actions$.ofType(GET_MODELS).switchMap(() => this.modelsApi.getModels()).map(models => ({type: SET_MODELS, payload: models})).catch((err: any) => Observable.of({type: GET_FAILURE, payload: {error: err}}))

现在我想做的是这样的:

@Effect() models$: Observable= this.actions$.ofType(GET_MODELS).do(() => this.store.dispatch({type: 'SET_LOADING_STATE', payload: true})).switchMap(() => this.modelsApi.getModels()).map(models => ({type: SET_MODELS, payload: models})).do(() => this.store.dispatch({type: 'SET_LOADING_STATE', payload: false})).catch((err: any) => Observable.of({type: GET_FAILURE, payload: {error: err}}))

如您所见,我们正在调度对 globalReducer (global.reducer.ts) 的调用:

export const GlobalReducer: ActionReducer

实现我想要的最佳方式是什么?

解决方案

isLoading 指示器存储在中央位置与有时使用错误信息进行的操作类似.一个存储中心错误的解决方案涉及使用一个reducer,它忽略动作的类型并且只查看它们是否包含<代码>错误属性.

如果您要为效果的动作类型采用合适的命名方案,您可以使用 isLoading 做很多相同的事情.

一种可能的命名方案可能是:

SOME_ACTION_REQUESTSOME_ACTION_RESPONSESOME_ACTION_ERROR

使用这样的方案,以下reducer 将检查动作类型并相应地设置isLoading 状态:

导出函数 isLoadingReducer(state: boolean = false, action: Action): boolean {如果 (/_REQUEST$/.test(action.type)) {返回真;} else if (/(_RESPONSE|_ERROR)$/.test(action.type)) {返回假;} 别的 {返回状态;}}

isLoading 使用 boolean 值假定您不会有并发的异步效果,因此如果这是一个问题,您可以扩展 reducer 以使用计数器.

如果你以这种方式连接起来,isLoading 指示器不需要知道关于单个效果的任何信息,反之亦然.

I want to achieve setting a global state while also requesting data from an api and storing that in the state as well, but in another location than the global state.

I'm calling this effect (models.effects.ts):

@Effect() models$: Observable<Action> = this.actions$
  .ofType(GET_MODELS)
  .switchMap(() => this.modelsApi.getModels())
  .map(models => ({type: SET_MODELS, payload: models}))
  .catch((err: any) => Observable.of({type: GET_FAILURE, payload: {error: err}}))

Now what I want to do is something like this:

@Effect() models$: Observable<Action> = this.actions$
  .ofType(GET_MODELS)
  .do(() => this.store.dispatch({type: 'SET_LOADING_STATE', payload: true}))
  .switchMap(() => this.modelsApi.getModels())
  .map(models => ({type: SET_MODELS, payload: models}))
  .do(() => this.store.dispatch({type: 'SET_LOADING_STATE', payload: false}))
  .catch((err: any) => Observable.of({type: GET_FAILURE, payload: {error: err}}))

As you can see we're dispatching a call to the globalReducer (global.reducer.ts):

export const GlobalReducer: ActionReducer<any> = (state: IGlobalStorage = {isLoading: false}, action: Action) => {

  switch(action.type) {

    case SET_LOADING_STATE: return Object.assign({}, state, {
      isLoading: action.payload
    });

    default: return state;
  }
}

Which would mean that I update the global state isLoading before and after we make an http request. However, this solution is both messy and does not work because of the simple fact that it breaks the effect (for whatever reason, I think it's because I call dispatch within the effect).

Preferably I would like to create another effect which listens to SET_LOADING_STATE which then calls the globalReducer itself, rather than letting the models$ effect do it directly.

Something like this (from within global.effects.ts):

@Effect() loadingState$: Observable<Action> = this.actions$
  .ofType(SET_LOADING_STATE)
  .do(() => ({type: SET_LOADING_STATE, payload: thePayloadThatWasSent}))

But there are 2 problems with that:

  1. I don't know how to access a sent payload in an effect.
  2. I don't know how to call that effect from within the models$ effect.

Overall I'm just really confused on how to achieve what I want and there aren't any examples of this as far as I've been able to find.

If you look at this image, I want to update global.isLoading when I update models:

What would be the best way to achieve what I want?

解决方案

Storing an isLoading indicator in a central place is a similar to what is sometimes done with error information. One solution for storing a central error involves using a reducer that ignores actions' types and looks only to see if they contain an error property.

If you were to adopt a suitable naming scheme for your effects' action types, you could do much the same thing with isLoading.

One possible naming scheme could be:

SOME_ACTION_REQUEST
SOME_ACTION_RESPONSE
SOME_ACTION_ERROR

With such a scheme, the following reducer would examine the action types and set the isLoading state accordingly:

export function isLoadingReducer(state: boolean = false, action: Action): boolean {

    if (/_REQUEST$/.test(action.type)) {
        return true;
    } else  if (/(_RESPONSE|_ERROR)$/.test(action.type)) {
        return false;
    } else {
        return state;
    }
}

Using a boolean value for isLoading assumes that you would not have concurrent asynchronous effects, so if that's an issue, you could extend the reducer to use a counter instead.

If you wire things up this way, the isLoading indicator doesn't need to know anything about individual effects and vice versa.

这篇关于Angular 2 更新全局状态作为更新某个状态的副作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆