user140503
user140503

Reputation: 127

How to detect when a chain of actions is done

For example, I dispatch an action. This action dispatches some other action and this is a kind of chain of actions. Depends on the input the chain of actions may be different. When I dispatch an action I do not know the last action so I can not subscribe to concrete action on its success. So my question is how can I get information that all actions are done?

actions:

export class ActionOne {
    static readonly type = 'Action one';
    constructor(public payload: boolean) { }
}

export class ActionTwo {
    static readonly type = 'Action two';
    constructor(public payload: boolean) { }
}

export class ActionThree{
    static readonly type = 'Action three';
    constructor(public payload: boolean) { }
}

state:

@Action(ActionOne)
actionOne(ctx: StateContext<StateModel>, action: ActionOne): void {
     ctx.dispatch(new ActionTwo());
}

@Action(ActionTwo)
actionTwo(ctx: StateContext<StateModel>, action: ActionTwo): void {
     if(payload) ctx.dispatch(new ActionThree()); else ctx.setState....
}

@Action(ActionThree)
actionThree(ctx: StateContext<StateModel>, action: ActionThree): void {
     ctx.setState....
}

action one dispatch

this.store.dispatch(new ActionOne(true)) // I don't know a path and last action, but I want to know when it is finished
this.store.dispatch(new ActionOne(false)) // I don't know a path and last action, but I want to know when it is finished

actions can be async!

Upvotes: 0

Views: 613

Answers (1)

Mateus Carniatto
Mateus Carniatto

Reputation: 340

I think you can accomplish this by using the action stream to track when your chain has been completed.

First off we need to be sure that the actions life cycle is bound and for this, we need to adjust a bit the code from your second snippet to:

@Action(ActionOne)
actionOne(ctx: StateContext<StateModel>, action: ActionOne): void {
     return ctx.dispatch(new ActionTwo()); // return dispatch here
}

@Action(ActionTwo)
actionTwo(ctx: StateContext<StateModel>, action: ActionTwo): void {
     if(payload) 
       return ctx.dispatch(new ActionThree()); // return the dispatch here
     else ctx.setState
}

@Action(ActionThree)
actionThree(ctx: StateContext<StateModel>, action: ActionThree): void {
     ctx.setState....
}

Notice that by returning the dispatch observable in your action handler you'll be binding the action life cycle properly. Meaning that the ActionOne will be only complete once the ActionTwo is completed.

Now you can watch for the completion of your actions using the action stream provided by NGXS. Like the snippet shown below:

import { Actions, ofActionCompleted } from '@ngxs/store'

@Injectable()
export class MyService {
  constructor(private action$: Actions) {
    this.action$.pipe(
      ofActionCompleted(ActionOne)  // filter the action you desire
      tap((x) => /* DO SOMETHING ABOUT IT */)
    )
  }
}

For the value x inside the tap you'll receive a object of type ActionCompletion<T> where T is the action type, in this case ActionOne. This interface can be seem ion the snippet below:

interface ActionCompletion<T = any> {
  action: T;
  result: {
    successful: boolean;
    canceled: boolean;
    error?: Error;
  };
}

Upvotes: 1

Related Questions