[SalesForce] Lightning navigation does not refresh the state of components, how to fix a bug

I am using lightning:navigation component in our project, and it doesnt refresh the state of component, when the url changes. To be clear, lightning framework just keeps the state of component/page and ignores url changes, even when we go through different records, page layouts etc.

I think its a bug, i opened a case, but we are just signed partners and dont have proper service support, even if we inform SF about a bug, this is sad 🙁

So here is the scenario.
We have action button on opportunity. When we click on it, it fires a method on ActionLauncher component, which redirects us from Opportunity Detail Page to our Container component. The url includes opportunity id, which we use in Container component to get some data from backend(apex).

ActionLauncher component:

<lightning:navigation aura:id="navigation"/>

ActionLauncher controller:

navigateToContainer : function(component, event) {
   var componentName = "Container";
   var recordId = component.get("v.recordId");
   var attributes = {"id": recordId};

   component.find("navigation")
    .navigate({
        "type": "standard__component",
        "attributes": {
            "componentName": componentName
        },
        "state" : attributes
    });
},

Container component:

<aura:component description="Container" implements="lightning:isUrlAddressable">

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

Container controller:

doInit : function(component, event, helper) {
   var pageReference = component.get("v.pageReference");

   if (pageReference) {
       var opportunityId = pageReference.state("id");
       component.set('v.recordId', opportunityId);

       //get some data from backend
   }
},

How I test it. I go to opportunity 1.
Click action button -> In ActionLauncher we input opportunityId to url -> Container component invoke doInit and grabs opportunityId url, get data from backend.

Then i go back to Opportunity 1 Detail Page -> to All Opportunities -> select Opportunity 2 -> click action button.
And now with another opportunityId -> ActionLauncher -> Container

And here we have new url with opportunity 2 Id, but container does not refresh the state, it keeps old state of opportunity 1 Id.

doInit of Container doesnt run again and i cannot use any events to fire it again, as we redirected to another page and our ActionLauncher belongs to Opportunity detail page.

And there is no proper way to inform Container component about changes in url.

IMHO when we use standard lightning navigation like that, lightning framework should always refresh components and run doInit method, if its there, I count this as a bug.

If I am wrong guys, just give me an example how to handle this situation :).

Meanwhile i am using a dirty solution, which helps me to sync Container content with url.

I added refreshData method to Container component, which invokes every second and checks the url opportunity id if it matches component opportunity id.

I dont like this solution, but i couldnt find anything better.

refreshData : function(component, event) {
    var pageReference = component.get("v.pageReference");

    if (pageReference) {
        var urlOpportunityId = pageReference.state("id");
        var componentOpportunityId = component.get('v.recordId');

        if (urlOpportunityId && urlOpportunityId != componentOpportunityId) {
            console.log("refreshData, run container doInit");
            this.doInit(component, event);
        }
    }

    setTimeout($A.getCallback(() => this.refreshData(component, event)), 1000);
},

Best Answer

We're running into this too. We expected it to be the case that if we navigate to a Lightning Component URL once, then walk through other pages, then navigate back to it with a different URL, we'll get a brand new instance and see the init method fire. Instead we get a stale older version with no init fired. So it's not clear what to do if what we really want is that new instance.

Will post back here if we find out exactly the best way to work around it.

EDIT: Aha! I found a relevant approach.

In the component with lightning:isUrlAddressable simply add a change handler for the pageReference state.

<aura:handler name="change" value="{!v.pageReference}" action="{!c.reInit}" />

And then your reInit method can reset the component back to whatever it needs to be. If you need more fine-grained logic (only reset it if a specific parameter changed, etc) then your reInit method can compare values as needed to produce the desired logic.

As far as I can tell in brief testing, the change handler does not fire on initial load (use your usual init method for that), nor on being reached via back/forward button. It fires on re-navigation to the component (even if the state parameters passed are identical) and it fires if the component internally uses lightning:navigation to change its own state.