By Ruslan Prytula August 13, 2015 8:54 PM
Sencha Touch/ExtJS: Avoiding memory leaks

Dear readers, in this post I will highlight few places where people usually make mistakes that cause memory leaks in Search Touch/ExtJS application.

Be careful with Ext.Element

Ext.Element has 2 “dangerous” methods: “up” and “down”. The reason why they are “dangerous”, is because resulting DOM-element, will be wrapped in Ext.Element instance, which will not be destroyed automatically.

When ST/ExtJS creates new instance of Ext.Element, it implicitly “registers” that instance in a global Ext.Element.cache (Ext.elements) object. So, whenever you want to use these methods, don’t forget to delete your implicitly created Ext.Element(s) from that object, to allow JS to free used memory. For instance:

Ext.define("MyComponent", {
  extend: "Ext.Component",
  config: {
    data: {},
    tpl: "<h1>My Component</h1>"
  },
  constructor: function() {
    this.callParent(arguments);
    this.element.down("h1").set({"data-title": "Title"});
  },
  destroy: function() {
    // !!! prevents memory leak.
    delete Ext.elements[this.element.down("h1").id];

    this.callParent(arguments);
  }
});

Of course, sometimes you might need only real DOM-element, without any functionality from Ext.Element class, so for these cases you always have a safe this.element.dom.querySelector(selector) option.

Usage of Ext.Component outside of Ext.Container#items

Most of the time the usage of Ext.Container helps you to avoid memory leaks problems, but there are many cases, when you shouldn’t use Ext.Container for your inner items. The most common one is when your markup is more complicated, so that your Ext.Component(s) is mixed with other DOM-nodes, for example:

<div class="profile">
  <h4 class="section">Basic Info</h4>
  <div class="basic-section-fields">
    <!-- ST/ExtJS components will be here.-->
  </div>
  <h4 class="section">Phones</h4>
  <div class="phones-section-fields">
    <!-- ST/ExtJS components will be here.-->
  </div>
  <h4 class="section">Emails</h4>
  <div class="emails-section-fields">
    <!-- ST/ExtJS components will be here.-->
  </div>
  <p class="hint">
    ...
  </p>
</div>

The best way to implement this interface, is to use Ext.Component instead of Ext.Container, have inner components stored in #config property and use appliers to instantiate them. The main problem in this approach is memory leaks which occur if you forget to manually destroy those components. Here is an example:

Ext.define("ComplexComponent", {
  extend: "Ext.Component",
  config: {
    data: {},
    tpl: Ext.create("Ext.Template",
      '<div class="profile">',
        '<h4 class="section">Basic Info</h4>',
        '<div class="basic-section-fields">',
          '<!-- ST/ExtJS components will be here.-->',
        '</div>',
        '...',
      '</div>', {
      compiled: true
    }),
    basicInfoComponent: {
      xtype: "view-profile-basic-info-component"
    }
  },
  applyBasicInfoComponent: function(config) {
    if(! config)
      return false;
    return Ext.factory(Ext.apply({
      // your default options go here.
      renderTo: this.element.dom.querySelector(".basic-section-fields")
    }, config));
  },
  updateBasicInfoComponent: function(newComponent, oldComponent) {
    if(oldComponent)
      oldComponent.destroy();
  },
  destroy: function() {
    this.callParent();

    // !!! prevents memory leak.
    this.setBasicInfoComponent(null);
  }
});

Thank you for reading! I hope this post can make your apps more stable and reduce their memory leaks count.