Sencha Performance Tips #1: Avoid Overnesting

As a member of the Professional Services Team here at Sencha, we are often asked for fix problems in our client’s apps. And the number one issue we can immediately detect is overnesting. But what’s overnesting, why it’s bad and how can we avoid it? Let’s check it out!

What is overnesting?

I’ve picked some quotes from our Sencha Forum users trying to verbalize what it is:

Overnesting is using an additional container that doesn’t actually do anything besides containing another component.

It adds no extra functionality, it’s a useless container…

Overnesting refers to nesting without reason.

Again, useless container…

Overnesting means have redundant panels in the hierarchy.

Good, has to do with the hierarchy…

Maybe it’s still not very clear…let’s see some code:

//example 1: TabPanel and GridPanel case
{
	xtype: 'tabpanel',
	items: [{
		title: 'Users Grid',
		layout: 'fit',
		border: false,
		items: {
			xtype: 'grid',
			store: 'Users',
			columns: [{
				header: 'Name',
				dataIndex: 'name'
			}]
		}
	}]
}

//example 2: TreePanel on Border Layout case
{
    xtype: 'panel',
    layout: 'border',
    items: [{
        xtype: 'panel',
        title: 'Navigation',
        layout: 'fit',
        region: 'west',
        width: 200,
        border: false,
        items: {
            xtype: 'treepanel'
            store: 'NavigationStore'
        }
    }, {
        xtype: 'panel',
        layout: 'fit',
        region: 'center',
        items: {
            xtype: 'tabpanel',
            border: false,
            items: [{
                title: 'Home Tab',
                html: 'Welcome!'
            }]
        }
    }]
}

//example 3: FormPanel case
Ext.define('App.view.UserForm',{
    extend: 'Ext.Panel',
    layout: 'fit',
    items: {
        xtype: 'form',
        items: [{
            fieldLabel: 'Name',
            name: 'name'
        },{
            fieldLabel: 'Email',
            name: 'email'
        }]
    }
});

All of the snippets above have overnesting issue. They all have one panel that shouldn’t be there. Since GridPanel, TreePanel and FormPanel are all Panels, there’s no need to wrap them in another Panel. The wrapper panel is useless.

First case we have a TabPanel with only one card. This card has the title “Users Grid”, and a grid inside of it fitting 100% of its space, thanks to fit layout. Since GridPanel is a Panel, it also understands the title config option. So we have a useless Panel here! This would be the right version:

//example 1 fixed
{
	xtype: 'tabpanel',
	items: [{
		xtype: 'grid',
		store: 'Users',
		title: 'Users Grid',
		columns: [{
			header: 'Name',
			dataIndex: 'name'
		}]
	}]
}

Second case we have a border layout Panel with 2 regions. The west region has a Panel entitled ‘Navigation’, with a TreePanel inside. This is our first occurrence of overnesting. The second is having a Panel on the center region, with a TabPanel inside of it. Since TabPanel is a Panel, we can directly put it as the center region.

//example 2 fixed
{
    xtype: 'panel',
    layout: 'border',
    items: [{
        xtype: 'treepanel',
        title: 'Navigation',
        store: 'NavigationStore',
        region: 'west',
        width: 200,
        border: false,
    }, {
        xtype: 'tabpanel',
        region: 'center',
        items: [{
            title: 'Home Tab',
            html: 'Welcome!'
        }]
    }]
}

The last case we defined a class that extends Panel, and inside of it we create a single FormPanel as child item. Simply subclassing FormPanel would solve the problem.

//example 3 fixed
Ext.define('App.view.UserForm',{
    extend: 'Ext.form.Panel',
    items: [{
        fieldLabel: 'Name',
        name: 'name'
    },{
        fieldLabel: 'Email',
        name: 'email'
    }]
});

And overnesting is not all about Panels! You can also overnest Containers inside Containers, or even Containers and Panels, like the examples below were we have useless Containers wrapping Fieldset and GridPanel.

//example 1 Container+Container
{
    xtype: 'form',
    items: [{
        fieldLabel: 'Name',
        name: 'name'
    },{
        xtype: 'container',
        items: {
            xtype: 'fieldset',
            items: [
                //...
            ]
        }
    }]
}

//example 2 Container+GridPanel
{
    xtype: 'container',
    region: 'center',
    layout: 'fit'
    items: {
        xtype: 'grid',
        //...
    }
}

What’s the problem with overnesting?

  • Extra panels means extra objects in memory and extra HTML markup
  • Code has more levels, and it’s more complex and less maintainable
  • With a bunch of nested panels you’re dangerously close to have layout problems. Forget to add layout to one of them, and then try to hunt your issue in this mess…not good!

How to detect and fix?

If you find yourself creating too many objects with layout: ‘fit’ and border: false, this could be one signal for overnesting. The basic rule is, watch out for Containers inside Containers. And this might be hard to see, specially because xtype: ‘panel’ is default on many objects definitions so we omit it. In the example below there’s an extra panel with title: ‘Category Form’, a form as child, and no xtype: ‘panel’ declaration. We could have promoted the form one level up and move the title config to it, saving one extra useless panel.

var tabPanel = Ext.create('Ext.TabPanel',{
    items: [{
        title: 'Category Form'
        items: {
            xtype: 'form',
            items: [
                //...
            ]
        }
    },{
        title: 'Categories'
        xtype: 'treepanel',
        store: 'CategoriesStores'
    }]
});

Also watch out for classes that extend Panel, like Ext.menu.Menu! Did you know you can create a Menu, set floating to false, and put it inside a border layout as the west region for navigation? No need to create a wrapping panel for it.

Ext.create('Ext.Window',{
    layout: 'border',
    width: 500,
    height: 300,
    autoShow: true,
    items: [{
        xtype: 'menu',
        floating: false,
        region: 'west',
        width: 300,
        items: [{
            text: 'Test'
        }]
    },{
        region: 'center'
    }]
});

So, we’ve seen today what is overnesting, how to detect, how to fix, and why it’s bad. Now we just need to do some detailed code reviews! :)