Managing Subviews in Backbone

The debate on how best to manage subviews in Backbone has always been relatively contentious. As intended, Backbone gives us the structure but not the solution, which has resulted in a myriad of different strategies created by the community.

Many people seem to be turning to other libraries to handle this for them, whether that's a stand-alone library such as Backbone LayoutManager or just a small part of an larger project such as Marionette or Chaplin.

My fundamental problem with these solutions however, is that they all require you to follow their patterns and learn the conventions. When working on a new project at Uber, I didn't want to commit to using one of these solutions because I didn't think we needed all of the overhead that came with them. I decided to roll with vanilla Backbone and cross the subview bridge when I came to it.

Within no time at all I was starting to worry about creating zombie views and decided that I really did need some simple way of managing subviews. After a little thought, I came up with these principals in order to evaluate the existing solutions or adhere to when creating my own.

I want to use a subview system that is:

  • Simple to use and understand for engineers unfamiliar with Backbone.
  • Not restrictive. It shouldn't force you to write your view code any differently.
  • Lightweight. Doesn't need a bunch of extra abstractions such as Layouts and Regions.

Poking around the internet I saw all sorts of implementations which broke these principles. People often seem to create a kind of SubView class which extends Backbone.View. To me, this is a complete anti-pattern. A view is only a subview because it is being drawn in side another one - not because it behaves any differently to a normal view. Every view should be able to be used as a subview.

With that in mind, it made sense to starting thinking about adding something to every view which allowed it to contain subviews. I decided in the end that I would have a BaseView which all of my other views extended from. I then added one simple function to that BaseView:

define(['underscore','backbone'], function (_, Backbone) {  
  'use strict';

  return Backbone.View.extend({

    _subviews: null,

    registerSubview: function(view){
      this._subviews = this._subviews || [];
      this._subviews.push(view);
    }

   });
});

All were doing here is simply keeping a reference to any subviews in an array. The only thing left to do is to automatically clean up all of those subviews when the parent view is removed. We can achieve that by simply overriding the remove method on the view, cleaning up our subviews, then calling the original remove method:

remove: function(){  
  _.invoke(this._subviews, 'remove');
  Backbone.View.prototype.remove.call(this);
}

That's it. That's subview management in about 10 lines of code. It lets you create Backbone views in any way you please, but helps you to ensure you don't get any zombies. All you have to do is to remember to register a subview any time you create one.

render: function(){  
   var rumView = new RumView();
   this.$el.append(rumView.render().el);
   this.registerSubview(rumView);
   return this;
}

That's not hard to remember, not a pain to write and most importantly, a very simple pattern to follow for new engineers on the project.

What's more, as all of the views extend BaseView, a single remove call on the top level view (let's say, on a page change) cascades all the way through the page cleaning every view.

I have no idea why there are multiple 100+ line subview management modules out there.