I'm using an application event to broadcast a global change. The event is fired once yet several handlers are triggered multiple times. All components are created purely via markup (e.g. none are generated in code).
This same problem was reported here https://developer.salesforce.com/forums/?id=9060G000000IAM6QAO back in March 2017. Two people commented they had a similar problem but no one offered a solution. I also found this and this. In both of these questions Rohit Kandul offered a solution but no one has confirmed the solution is ok.
- what is the lifecylce of components created via markup.
- when are components destroyed? or better when are their event handlers disabled.
- is it appropriate and is it safe to use Rohit's solution. Which is:
In the RENDERER file put the below code
({ unrender: function(component){ this.superUnrender(); component.destroy(); } })
UPDATE:
Background information that may help. I've created a "single component app" for Cases. It contains many child components all declared in the markup. The main component is placed on the Case record page and supports staff who manage emails and case resolution. When the user selects a Case from the Case list view the Case record page is displayed and my component hierarchy is created.
I assumed these components would only be created and initialized once. But each time the user selects a new Case a whole new set of components are ... I'm not sure what to say but I know that each's init handler is called.
The multiple event problem above is simply because there are multiple components, in memory, that are duplicates, that are registered to handle the application event.
UPDATE2 Each component has a unique id. Using this
var thisId = component.getGlobalId();
I can see that only four copies of each component are present in the system. I can also see they all share the same data. Why does LE need duplicates of what are essentially singletons? Why create a new Javascript object that shares data with it's brethren? The end result is there are four copies of every component in my system and since they respond to application events and make server calls there is a great deal of wasted resources and sluggishness built into the system. I can see that we won't run out of memory.
UPDATE 3
Some have suggested to manually destroy these duplicate instances based on the location change event. But it seems from here that this event is flakey and unpredictable and from here there is a known issue with location change and IE11.
It also seems from here that manually destroying these duplicate instances is also flakey and unpredictable. (And besides why should the developer be messing with the framework this way?)
I've also found another original question that is a duplicate of mine here
And another duplicate here
Update 4:
I was at first very hopeful with Praveen's answer and that he had help from SF but it does not work because the duplicate components actually share the same data. Below are simplified debug statements from one component as it responds to application events GC, AL, EE and EV, plus the init event IN.
For IN there is only the record id as passed into the component via markup <c.mycmp recordId=...>
. The next id is the record id coming from the event from the component that emits the event. Which, of course, is its own copy of record id that it got from initialization. Component cmpA is initialized first. It get events GC and AL. I then select another tab with another record and cmpB is initialized. See how cmpA still responds to the events but now it's local copy of record id has been replaced. I was very careful to stringify all debug statements so that we don't have the browser showing us live data.
I then click on a third tab and we have three active components: cmpA, cmpB and cmpC ALL with the same record id!!!!
IN "cmpA, id1"
GC "cmpA, id1, id1
AL "cmpA, id1, id1
IN "cmpB, id2"
GC "cmpA, id1, id2"
GC "cmpB, id2, id2
AL "cmpA, id2, id2
AL "cmpB, id2, id2
AL "cmpA, id2, id2
AL "cmpB, id2, id2
IN "cmpC, id3"
GC "cmpA, id2, id3"
GC "cmpB, id2, id3"
GC "cmpC, id3, id3
AL "cmpA, id3, id3
AL "cmpB, id3, id3
AL "cmpC, id3, id3
AL "cmpA, id3, id3
AL "cmpB, id3, id3
AL "cmpC, id3, id3
AL "cmpA, id3, id3
AL "cmpB, id3, id3
AL "cmpC, id3, id3
UPDATE 5
I may have found a solution. That GC event represents an application event that broadcasts global data is ready. This data contains the Case Number. (I'm just working with Case records.) Even though all three visible components are showing the same record id (which is just wrong) they do show different and correct case numbers. Now if I can pass the case number in the events I might be able to determine which component should process which event. I can't explore this further for now but it might help to consider what is different between the record id and case number. The record id is set via markup as an attribute of the component; passing the record id from the parent down to the child. In my situation, the case number is coming via a broadcast event when global data is ready. The child component receives this data and stores a piece of it (the case number) in a component attribute. Somehow this round about way of setting the value is different. Probably because LEX is creating bindings between the markup declared attribute value. Yes. this might be the next stage of exploration.
UPDATE 6 Greg Grinberg suggests passing in the case ID via an UNbound expression to avoid the data binding {#case.id}
{#case.id}
. Might resolve this issue with dupe components all having the same ID.