NgRx status update and effects order

I have my opinion on this issue, but it is better to check and find out for sure. Thank you for your attention and trying to help. Here he is:

Imagine that we are sending an action that causes some state changes and also has some effects attached to it. Therefore, our code should do 2 things - change state and do some side effects. But what is the order of these tasks? Do we do them synchronously? I believe that at first we change the state, and then we make a side effect, but is there any chance that something else can happen between these two tasks? For example: we change the state, then we get the response to the HTTP request that we made earlier, and we process it, and then we make side effects.

[edit:] I decided to add the code here. And also I simplified it.

Condition:

export interface ApplicationState { loadingItemId: string; items: {[itemId: string]: ItemModel} } 

Actions:

 export class FetchItemAction implements Action { readonly type = 'FETCH_ITEM'; constructor(public payload: string) {} } export class FetchItemSuccessAction implements Action { readonly type = 'FETCH_ITEM_SUCCESS'; constructor(public payload: ItemModel) {} } 

Dilution:

 export function reducer(state: ApplicationState, action: any) { const newState = _.cloneDeep(state); switch(action.type) { case 'FETCH_ITEM': newState.loadingItemId = action.payload; return newState; case 'FETCH_ITEM_SUCCESS': newState.items[newState.loadingItemId] = action.payload; newState.loadingItemId = null; return newState; default: return state; } } 

The effect:

 @Effect() FetchItemAction$: Observable<Action> = this.actions$ .ofType('FETCH_ITEM') .switchMap((action: FetchItemAction) => this.httpService.fetchItem(action.payload)) .map((item: ItemModel) => new FetchItemSuccessAction(item)); 

And this is how we send the FetchItemAction:

 export class ItemComponent { item$: Observable<ItemModel>; itemId$: Observable<string>; constructor(private route: ActivatedRoute, private store: Store<ApplicationState>) { this.itemId$ = this.route.params.map(params => params.itemId); itemId$.subscribe(itemId => this.store.dispatch(new FetchItemAction(itemId))); this.item$ = this.store.select(state => state.items) .combineLatest(itemId$) .map(([items, itemId]: [{[itemId: string]: ItemModel}]) => items[itemId]) } } 

Desired scenario:

 User clicks on itemUrl_1; we store itemId_1 as loadingItemId; make the request_1; user clicks on itemUrl_2; we store itemId_2 as loadingItemId; switchMap operator in our effect cancells previous request_1 and makes request_2; get the item_2 in response; store it under key itemId_2 and make loadingItemId = null. 

Bad scenario:

 User clicks on itemUrl_1; we store itemId_1 as loadingItemId; make the request_1; user clicks on itemUrl_2; we store itemId_2 as loadingItemId; we receive the response_1 before we made the new request_2 but after loadingItemId changed; we store the item_1 from the response_1 under the key itemId_2; make loadingItemId = null; only here our effect works and we make request_2; get item_2 in the response_2; try to store it under key null and get an error 

So the question is, can a bad script just happen or not?

+7
javascript angular ngrx ngrx-effects ngrx-store
source share
1 answer

So, our code should do 2 things - change state and make some side of the consequences. But what is the order of these tasks? Do we do them synchronously?

Say we send action A. We have several reducers that handle action A. They will be called in the order in which they are specified in the object that is passed to StoreModule.provideStore (). Then a side effect will be triggered that listens to action A. Yes, it is synchronous.

I believe that at first we change the state, and then we make a side effect, but is there any possibility that something else can happen between these two tasks? For example: we change the state, then we get the response to the HTTP request that we made earlier, and we process it, and then we make effects.

I have been using ngrx since the middle of last year and I have never seen this to be the case. What I discovered is that every time an action is sent, it goes through the entire cycle, first processed by gearboxes, and then side effects before the next action is processed.

I think this should be the case because redux (from which ngrx evolved) acts as a predictable state container on its main page. By providing unpredictable asynchronous actions, you cannot predict anything, and decix dev tools will not be very useful.

Edited # 1

So I just did a test. I performed the β€œLONG” action, and then a side effect performed the operation, which takes 10 seconds. At the same time, I was able to continue using the user interface, making more posts in the state. Finally, the LONG effect is finished and sent to LONG_COMPLETE. I was mistaken that reducers and side effects are a transaction.

enter image description here

This says that I think it’s still easy to predict what happens because all state changes are still transactional. And this is good, because we do not want the user interface to lock, waiting for a long api call.

Edited # 2

So, if I understand this correctly, the core of your question is about switchMap and side effects. Basically you ask, what if the answer comes back the moment I run the reducer code, which then triggers a side effect using SwitchMap to cancel the first request.

I came up with a test that seems to answer this question. The testing I installed was to create 2 buttons. One is called Quick and one is called Long. Fast send "FAST", and Long will send "LONG". The reducer that listens to Quick will terminate immediately. The gear that listens to Long takes 10 seconds.

I install one side effect that listens to both Quick and Long. This claims to emulate an api call using "of", which allows me to create observables from scratch. Then it will wait 5 seconds (using .delay) before sending "QUICK_LONG_COMPLETE".

  @Effect() long$: Observable<Action> = this.actions$ .ofType('QUICK', 'LONG') .map(toPayload) .switchMap(() => { return of('').delay(5000).mapTo( { type: 'QUICK_LONG_COMPLETE' } ) }); 

During my test, I pressed the shortcut button and then immediately pressed the long button.

Here's what happened:

  • Quick button pressed
  • 'QUICK' sent
  • A side effect starts observable, which will be completed in 5 seconds.
  • Long button pressed
  • sent "LONG".
  • LONG gearbox processing takes 10 seconds. At around 5 seconds, the original observed from the side effect ends, but does not send QUICK_LONG_COMPLETE. Another 5 seconds pass.
  • A side effect that hears "LONG" disables my first side effect.
  • 5 seconds elapse and "QUICK_LONG_COMPLETE" is sent.

enter image description here

Therefore switchMap cancels and your bad case should never happen.

+6
source share

All Articles