Reliable way to clear a setInterval used by a Lightning Component?

Usually, the component does not get destroyed once you navigate to a different view. When I tested at my end it usually takes around 9 distinct navigation(eg: visiting 9 different tabs) for the component to become invalid (i.e destroy).

In general, when component is destroyed it is necessary to remove event handler or callbacks attached to the window,document,.. objects. The right place to do it is in the unrender handler of a component as mentioned here

You could do two things:

1) Use unrender to clear the setInterval. (NOTE: Even though it does not get cleared once you navigate. Since each view has its own version of the window object (see below attachment), I think it might not be issue).

2) Listen to aura:locationChange event and clear the interval.

Below

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
<aura:attribute name="counter" type="Integer" default="0"/>
<aura:attribute name="setIntervalId" type="Integer"/>
<aura:handler name="init" value="{!this}" action="{!c.initMethod}" />
<aura:handler event="aura:locationChange" action="{!c.handleDestroy}"/>

    Counter : {!v.counter}
</aura:component>

controller.js

({
    initMethod : function( cmp, evt, h ) {
        var i = 0;
        var interval = window.setInterval(
            $A.getCallback(function() {

                var value = i++;
                console.log(value + ' from component with id ' + cmp.getGlobalId());
                // code to execute periodically goes here
                cmp.set("v.counter", value);

            }), 2000
        );   


        cmp.set("v.setIntervalId", interval) ;   

    },
    handleDestroy : function( cmp, evt, h ) {
        console.log('clear interval due to navigation');
        window.clearInterval(cmp.get("v.setIntervalId"));
    },
})

renderer.js

({
    // Your renderer method overrides go here
    unrender: function (cmp,helper) {
        this.superUnrender();
        console.log('clear interval due to unrender');

        window.clearInterval(cmp.get("v.setIntervalId"));
    }
})

An interesting note, when I added the above component to two tabs in LEX; each component's setTimeout logs separately without overriding each other. Looks like Lightning creates window instance for a view in LEX.

enter image description here


Try the component.isValid() method. Pass a reference to the component into the method that executes on the interval. Have it check for validity every time it runs.

https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cmp_isvalid.htm

So adapting that code...

init : function(cmp){
    var i = 0;
    var interval = window.setInterval(
        $A.getCallback(function() {
            if (!cmp.isValid()){
                window.clearInterval(interval);
                console.log('Component no longer valid!');
                return;
            }
            // code to execute periodically goes here
            console.log(i++);
        }), 2000
    );     
    // other init code goes here
}

Once the component has been unrendered the next validity check should fail.

I tested this by pasting it into the init handler of an existing component and then I monitored my browser console while going into Lightning App Builder. When I added the component to the page I could see the counter ticking in console. Once I clicked X to remove the component I got the "no longer valid" message and it stopped ticking.