Platform: The way to fire an effect after effects are settled up

Created on 8 Aug 2017  路  10Comments  路  Source: ngrx/platform

I'm submitting a...


[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request

What is the current behavior?


There is no action, no function which fired when all effects are settled up. Where we can dispatch actions to fire other effects.

Expected behavior:

    this.onInit$ = this.actions$
      .ofType('@ngrx/effects/up')
      .switchMap((isAuthorized) => {
        return this.isAuthorized$;
      })
      .map((isAuthorized) => {
        if (isAuthorized) {
          this.store.dispatch({ type: '[posts] getAll' })
        }
      });

    @Effect()
    this.onGetPosts$ = this.actions$
      .ofType('[posts] getAll')
      .map(() => ... )

or, may be

  public ngrxOnRunEffects(resolvedEffects$: Observable<EffectNotification>): Observable<EffectNotification> {
    this.isAuthorized$
      .take(1)
      .subscribe({
        next: (isAuthorized) => {
          if (isAuthorized) {
            this.store.dispatch({ type: '[posts] getAll' })
          }
        },
      });
    return resolvedEffects$;
  }

Minimal reproduction of the problem with instructions:


There a few issues, of @ngrx/store/init action. Seems, that this action is not useful for described purpose anymore. So, we probably need another.

103

152

Version of affected browser(s),operating system(s), npm, node and ngrx:

ngrx v4

Other information:

Possible workaround:

dispatch action, somewhere after application start.
In the AppComponent for example.

this.store.dispatch({ type: '[app] init' });

And use it to dispatch other actions, that must be dispatched after all effects are settled up.

Accepting PRs Effects

Most helpful comment

Not sure why this would be needed. Have you tried the following operator:

 this.onInit$ = this.actions$
      .ofType(Actions.REESTABLISH)
      .startWith(new Actions.ReestablishAction()) // This triggers the specified action on load.
      .switchMap((isAuthorized) => {
        return this.isAuthorized$;
      })
      .map((isAuthorized) => {
        if (isAuthorized) {
          this.store.dispatch({ type: '[posts] getAll' })
        }
      });

All 10 comments

Not sure why this would be needed. Have you tried the following operator:

 this.onInit$ = this.actions$
      .ofType(Actions.REESTABLISH)
      .startWith(new Actions.ReestablishAction()) // This triggers the specified action on load.
      .switchMap((isAuthorized) => {
        return this.isAuthorized$;
      })
      .map((isAuthorized) => {
        if (isAuthorized) {
          this.store.dispatch({ type: '[posts] getAll' })
        }
      });

@wesselvdv
What is Actions.REESTABLISH and Actions.ReestablishAction?
I don't see such variables in ngrx's Actions.

Also, I don't think, that I need to initialize first value in stream with startWith.
I need values from this.isAuthorized$, but this observable must wait until ngrx/effects are up.


I can write that in the service constructor

    this.isAuthorized$
      .take(1)
      .subscribe({
        next: (isAuthorized) => {
          if (isAuthorized) {
            this.store.dispatch({ type: '[posts] getAll' })
          }
        },
      });

[posts] getAll action fill be fired. But it must trigger an event, and no events are listening yet.

@ValeryVS Those are placeholder action classes. You're using strings as actions, while the accepted way to define an action is using a class with a type parameter that contains a string.

Something like this from the example app:

export class LoginAction implements Action {
  readonly type = LOGIN;

  constructor(public payload: Authenticate) {}
}

Doing it like above gives you type checking on the payload. I just used a placeholder that follows that convention.

Anyhow, back on topic; where is isAuthorized coming from? Is that an initial state defined in a reducer? What is it?

@wesselvdv
Yes, initial state of isAuthorized is defined in a reducer.

Now I wrote this

    @Effect({ dispatch: false })
    this.onInit$ = this.actions$
      .ofType('[app] init')
      .switchMap(() => this.isAuthorized$)
      .map((isAuthorized) => {
        if (isAuthorized) {
          this.accountService.fetchAll();
          this.imgService.fetchAll();
          this.personService.fetchAll();
          this.roleService.fetchAll();
        }
      });

    public init() {
      this.store.dispatch({ type: '[app] init' });
    }

It works, but I need to laucn init() function in AppComponent. Through that I will know, when app is started and all ngrx effects are listen.

I suggest to make action, that will be dispatched automatically.
Before v4 it was init event https://github.com/ngrx/platform/issues/103#issue-243915851, but now it isn't.

That's way too complicated for this. The only thing you want is the initial state of a reducer, and you want to fire a boatload of actions based off it.

You can get the current state of a reducer using an operator:

@Effect()
    this.onInit$ = this.actions$
      .ofType('[app] init')
      .startWith('[app] init')
      .withLatestFrom(this.store.select(state => state.isAuthorized)) // < ---- Not sure where your prop is exactly.
      .map(([, isAuthorized]) => {
        if (isAuthorized) {
          this.accountService.fetchAll();
          this.imgService.fetchAll();
          this.personService.fetchAll();
          this.roleService.fetchAll();
        }
      });

Where you'll need to inject the store in your constructor as you would normally do to access a state from anywhere.

I will accept a PR that adds an "Root Effects Init" action that gets dispatched after the root effects have been started.

I have a working branch for this but I'm not 100% sure if its correct.
Is it OK if I submit a PR and we'll take it from there :smile: ?

EDIT: you can take a look at this branch - or just the commit.

@tdeschryver any movement on this?

@brandonroberts Didn't change anything since last time.
The problem is that I'm not 100% certain about the solution.
Is it OK to open a PR and take if from there?

@tdeschryver yes, that's fine.

Was this page helpful?
0 / 5 - 0 ratings