Search code examples
angularngoninitlifecycle-hook

How do I run a function on ngOnInit() only once and not again upon returning back from another routerLink?


HomeComponent

ngOnInit()
{
    console.log('loaded');
    this.retrieveData();
}

retrieveData()
{
    // this.dataService.getData().subscribe(...);
}

I am retrieving data when the component loads. When the user clicks on another routerLink, for example, SettingsComponent and returns back to the HomeComponent, the function of course, is called again because the component has loaded again. But Everytime I return back to the component, it makes the function call again which creates too many unwanted HTTP requests. I need to prevent this and make sure the function is called only the first time. How do I do this? Should I use some other component lifecycle hook?


Solution

  • Okay I see you are using a service to load the data which is a good way.

    Then you can simply cache the data somewhere and when you come back to a component check the cache for this data. I think you can store the data directly in your service which will keep it in the memory or you can put it in the localStorage

    So first option would look like:

    data.service.ts

    export class DataService {
    
        private data: any[];
    
        setData(data:any[]){
            this.data = data;
        } 
    
        getData(){
            return this.data || [];
        }
    
        hasData(){
            return this.data && this.data.length;    
        }  
    
        getData(){
            // your implementation here 
        }
    }
    

    then inside HomeComponent

    retrieveData(){
        if(this.dataService.hasData()){
             // this will get the data which was previously stored in the memory
             // and there will be no HTTP request
             let data = this.dataService.getData();
             // do something with data now ...
        }else{
            // old code 
            this.dataService.getData().subscribe(response => {
                // but this time safe the data for future use
                this.dataService.setData(response.data);   
            }, error => {
                // handle errors
            });
        }
    }
    

    IMPORTANT: if using this approach you should make your service global when declaring it in app.module.ts -> providers

    @NgModule({
        declarations: [
            AppComponent
        ],
        imports: [
            BrowserModule,
            FormsModule,
            HttpModule
        ],
        providers: [
            DataService   <---------- SEE HERE
        ],
        bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    and then not doing this:

    @Component({
        selector: 'home',
        templateUrl: '...',
        styleUrls: ['...'],
        providers: [
            DataService     <---- THEN DON'T put in component's providers
        ]
    })
    export class HomeComponent{ ... }
    

    =============================================

    The localStorage approach

    HomeComponenet

    retrieveData()
    {
        let data = localStorage.getItem('yourDataName');
        if (data === null){
            // old code 
            this.dataService.getData().subscribe(response => {
                // but this time safe the data for future use in localStorage
                localStorage.setItem('yourDataName', response.data); 
            }, error => {
                // handle errors
            });
        } else {
            // seems that you already loaded this data 
            // do something with this data ...
        }
    }
    

    Both approaches has the limitation that you can't work with huge amounts of data, of course if you don't want to blow the browsers of users that are using your app :)