Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dispatch action on registration of feature effects #683

Closed
Manduro opened this issue Jan 4, 2018 · 12 comments · Fixed by #1305
Closed

Dispatch action on registration of feature effects #683

Manduro opened this issue Jan 4, 2018 · 12 comments · Fixed by #1305

Comments

@Manduro
Copy link

Manduro commented Jan 4, 2018

I'm submitting a...


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

What is the current behavior?

My application has multiple feature modules (with store and effects using forFeature) that are not lazily loaded. I want to run an effect on initialization inside such a feature module.

Example:
app.module -> EffectsModule.forRoot([CoreEffects])
auth.module -> EffectsModule.forFeature([AuthEffects])

class AuthEffects {
  @Effect()
  init$ = this.actions$
    .ofType(ROOT_EFFECTS_INIT)
    .pipe(tap(() => window.alert('I should be shown')));

  constructor(private actions$: Actions) {}
}

This init effect is never run because the ROOT_EFFECTS_INIT action is dispatched before the AuthEffects are registered with ngrx.

Expected behavior:

It should be possible to run an initial effect in a feature module.

Minimal reproduction of the problem with instructions:

https://stackblitz.com/edit/ngrx-feature-init-effect
This demo includes the CoreEffects and AuthEffects from the example above. Both include an effect for ROOT_EFFECTS_INIT. Only the core init effect runs.

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

Latest ngrx

@brandonroberts
Copy link
Member

I would consider this a feature and not a bug as the effects registered in the forRoot method are working as designed.

@Manduro
Copy link
Author

Manduro commented Jan 6, 2018

But are the effects registered by forFeature working as designed as well? Anyway, bug or feature, I expected it to work like this. Would be nice to have!

As a workaround I'm just registering all non-lazy feature module effects in the core module using forRoot.

@brandonroberts
Copy link
Member

Yes, feature effects don't currently dispatch an action when registered. You would receive multiple actions dispatched based on how many feature modules were loaded. Instead you would dispatch an action in your feature module to trigger your effects.

@kylecordes
Copy link

kylecordes commented Mar 4, 2018

I have been working around this by having a component or service in my feature module dispatch an event at (feature) startup; it does feel a bit tacky compared to the official automatic event dispatched at application startup.

So regardless whether this is a bug or feature, it would indeed be nice to have something parallel to the application level automatic action be dispatched automatically upon feature loading.

@brandonroberts
Copy link
Member

I think adding this is reasonable, the issue is what to dispatch. If we dispatch a generic { type: '@ngrx/effects/feature' } action, then you still don't have any specific information to key off of. We could add additional metadata to the dispatched action, but where does that get registered?

EffectsModule.forFeature([
  Effect1,
  Effect2
], {
  initAction: { type: 'EffectsRegistedAction' }
})

I don't love it, but its a start.

@visurel
Copy link

visurel commented May 17, 2018

Any updates on this? Seems like a needed feature in many cases.

@brandonroberts brandonroberts self-assigned this May 23, 2018
@brandonroberts
Copy link
Member

I'll look into this again after we launch V6

@timdeschryver
Copy link
Member

I think adding the following should work too for now?

@Effect()
init = this.actions.pipe(
  startWith({ type: 'INIT_MY_FEATURE' }), 
  take(1)
);

@danielhdz56
Copy link
Contributor

The problem with this is that the action won’t get registered when using start with (in your logger).

@onebrother21
Copy link

onebrother21 commented Aug 3, 2019

The "startsWith, take(1)" solution is pretty solid. To achieve the desired effect, add an init action to your feature:

export const TOOLS_EFFECTS_INIT = createAction('[Tools] Init');

Then use a switchMap to dispatch the init action followed by the real actions needed upon init:

init$ = createEffect(() => this.actions$.pipe( startWith({type:"INIT_TRIGGER"}), take(1), switchMap(() => [ TOOLS_EFFECTS_INIT(), populateRiskFree()])));

Great workaround @timdeschryver

@onebrother21
Copy link

onebrother21 commented Aug 3, 2019

Quick note/correction. If you are using the router store, you may notice that your init actions will fire but the actual effect is not triggered due to your app's initial navigation actions. Adding a slight delay will ensure that side effects of your initial effects/actions fire as well.

init$ = createEffect(() => this.actions$.pipe( startWith({type:"INIT_TRIGGER"}), take(1), delay(100), switchMap(() => [ TOOLS_EFFECTS_INIT(), populateRiskFree()])));

@nbhat000
Copy link

nbhat000 commented Aug 4, 2021

Late to the game, but if anyone stumbles upon this, NgRx Effects now has an OnInitEffects lifecycle hook that can be used in the effect to accomplish this functionality: https://ngrx.io/api/effects/OnInitEffects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants