Why should we re-attach the event listeners from an old script instance when it can swap?

Today I am learning how to use hot-swapping. I was puzzled when I read the following passage:

It is also important to remove any event listeners from the old instance and re-attach them to the new one.

I tried to comment out this code to run the program, and I manually turned on and off the script during the running of the program, without any errors, the ‘onEnable’ event can still be executed normally, so what is the point of re-attaching the event listeners??

// remove the old event listener
old.off('enable', this._onEnable);
// add a new event listener
this.on('enable', this._onEnable);

Hot swapping is different from disabling/enabling a script.

With hot swapping, the engine is creating a new instance of the script hence the need to off/on the event. Here, we are removing the listeners on the old script instance adding new ones to the new script instance.

With enabling/disabling a script instance, it is still the same instance attached to the entity.

I get it we need to reattach the events because its a new script copy and therefore doesn’t know what we did previously in the initialize function because it doesn’t run twice. But why bother letting the old events bound? I did a small test here and my events didn’t fire twice…(i don’t know if i did this wrong)

By the way i have another doubt…if i have the previous script assigned to a variable in another script like:

//original script
AnotherScript.prototype.initialize = function(){
this._otherScriptRef = pc.app.root.findByName("EntityThatHotReloads").script.hotReloadScriptName

if the hotReloadScriptName refreshes due hotReload every function “AnotherScript” runs from "otherScriptRef " will be useless since this reference is outdated? Should i make an event to refresh "otherScriptRef " referece if a hot reload happens in the other code? Like:

this._otherScriptRef = pc.app.root.findByName("EntityThatHotReloads").script.hotReloadScriptName;

  this._otherscriptRef.on("hotReloadTriggered", function(){
  this._otherScriptRef = pc.app.root.findByName("EntityThatHotReloads").script.hotReloadScriptName;
  }, this)

It’s because of the way the event system works. The object being subscribed to doesn’t have any idea what is being subscribed to it so the listeners have to manually be unsubscribed to it otherwise they would be dangling/orphaned.

1 Like

So this also applies to other scripts references…like in the example i gave above? During the hotReload from “hotReloadScriptName” since it has a reference on “AnotherScript” i should call


in order to make sure during hot reload the other script can update its own value?

It’s bit difficult to tell without the full project to look at as an example but basically, assume the scriptType instance is removed and a new one is added. So if you are referencing it directly from else, it would need updating.

You could also reference the entity or script component for simplicity and that way you don’t need to update references during hotswaps

Yeah…this is my third week i am studying play canvas for a job and i’m starting to think is way better listen inputs from this.entity.on rather than this.on (in cases a want to keep the events between objects instead of global events using this.app.on)because I feel dirty referencing the scripts directly and in a bigger project will have a lot of headches if one script needs to be deleted/replaced

I tend to follow the general rule that that entities can reference anything of their children, scripts, components etc as you can move that entity and it would still work. It’s also relatively straightforward to debug as you know it’s only handling itself and it’s children.

Inversely children can only broadcast out and upwards. They don’t have any direct references to anything that aren’t their children.

This keeps the data flow easy to follow and scenes more manageable.

You can see me follow those principles here: https://playcanvas.com/project/950571/overview/fps

1 Like