Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
461 views
in Technique[技术] by (71.8m points)

rxjs - Angular 2 update global state as a side effect of updating a certain state

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:

redux dev tools

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...