5

Just a generic question regarding the two Lifecycle Hooks OnInit and OnDestroy. As this article mentions, I had always assumed that OnInit is always run before the OnDestroy.

I have a case where in an ngOnDestroy() I'm stopping a background music track from playing. This sound is loaded the component's ngOnInit(), and because the ngOnDestroy() is being run without the ngOnInit() being run the sound object is undefined.

Code

ngOnInit() {
    ...

    this.loadSounds();

    ...
}

ngOnDestroy() {
    if (AppSettings.SOUNDS_ENABLED) {
        this.soundService.getSound(Sound.MINI_GAME_BG_MUSIC).fade(0.2, 0, 1500);
    }
}

private loadSounds() {
    this.soundService.loadSound(Sound.MINI_GAME_BG_MUSIC, SoundPathURL.MINI_GAME_BG_MUSIC, true, 0);
}

At the moment the code is failing in the ngOnDestroy when trying to .fade() a sound that's undefined. Of course I can easily fix this by checking that the sound is not undefined before executing the .fade() function. My assumption was that if an ngOnDestroy() is run the ngOnInit() must have been run as well - I guess I was wrong.

Now because of this case I'm thinking that in every ngOnDestroy() in my application I should check whether the object being used is undefined before performing any operations. So for example before unsubscribing from a subscription I should check if the subscription is undefined, and so on.

Am I right to assume so?

Daniel Grima
  • 2,765
  • 7
  • 34
  • 58

1 Answers1

5

The reference clearly states that OnInit lifecycle hook precedes OnDestroy. This behaviour is specific to the compiler in general. The article explains the behaviour that is specific to Angular router and the way <router-outlet> directive instantiates components. router.navigateByUrl() in route component constructor triggers its immediate destruction and prevents it from being compiled, so the lifecycle isn't applicable to it. This won't happen under normal circumstances.

The code in the question doesn't imply that ngOnInit doesn't run. ngOnInit call be easily logged to prove that it runs.

The service that is used by this component can cause the problem. The fact that Cannot read property 'fade' of undefined or similar error is thrown in ngOnDestroy means that this.soundService.getSound(Sound.MINI_GAME_BG_MUSIC) is undefined.

This totally depends on what happens inside soundService service. If loadSound is asynchronous, and ngOnDestroy was called before a resource was loaded, there may be problems.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks for your reply. I just found the cause for my issue. The component is dynamically created and I just realised that due to some refactoring I was doing I forgot to remove some old code and the component was being created twice. I'm still interested in knowing if the destroy can ever run before the init lifecycle hooks. As I mentioned I have assumed that it works like so, although in the article I linked it seems that it could occur. – Daniel Grima Nov 13 '17 at 15:50
  • 1
    The only reason why OnInit isn't triggered in the article you've posted is that proper component compilation is prevented in `constructor` because the router was messed up. This situation is very specific to Angular router and not to the compiler in general. The router forcefully destroys component class without compiling it. This won't happen under normal circumstances. I'll add this to the answer for clarification. – Estus Flask Nov 13 '17 at 16:28