Angular 2 updates global state as a side effect of updating a certain state

I want to set the global state, as well as request data from the api and save it in a state, but in a place other than the global state.

I call 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 I want to do 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 are sending a call to 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; } } 

This would mean that I am updating the isLoading global state before and after we make the http request. However, this solution is dirty and does not work due to the simple fact that it violates the effect (for some reason, I think, because I call sending within the effect).

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

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

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

But there are two problems with this:

  • I do not know how to access the transferred payload as a result.
  • I do not know how to cause this effect due to the models$ effect.

In general, I am just very confused about how to achieve what I want, and there are no examples of this as far as I can find.

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

reduction tools

What is the best way to achieve what I want?

+3
source share
1 answer

Saving the isLoading indicator in a central place is similar to what is sometimes done with error information. One solution to storing a central error involves using a reducer that ignores action types and only searches if they contain the error property.

If you must adopt a suitable naming scheme for the types of actions of your effects, you can do the same with isLoading .

One possible naming scheme may be:

 SOME_ACTION_REQUEST SOME_ACTION_RESPONSE SOME_ACTION_ERROR 

With such a scheme, the following reducer will check the types of actions 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 the boolean value for isLoading assumes that you will not have simultaneous asynchronous effects, so if this is a problem, you can expand the reducer to use a counter instead.

If you connect things this way, the isLoading indicator should not know anything about individual effects and vice versa.

+3
source

Source: https://habr.com/ru/post/1272695/


All Articles