Quick Introduction

Some people say that recursion is hard to digest. In terms of software development, I don't feel like it is. A simple definition would be that recursive function is a self-invoking function, meaning that it will invoke itself at a certain point of its execution.

A more theoretical definition would be that recursion is a behavior requiring two properties:

  • A base case - the case which will stop the recursion
  • A set of rules responsible for reducing all cases towards the base case

I cannot decide which one is more important. Without the base case, the recursion will become an infinite loop and yet without the reducing cases towards it, we cannot achieve the desired behavior. Anyway, you need both in order to make it function properly.

Recursion and Vue Components

In Vue, recursion is very much possible and quite useful. I mean, not only in Vue, we can follow the rules above to implement recursive behavior in any framework. So, based on the given definition, we can say that a recursive component is a component invoking itself.

When is this useful? Well, whenever you need to use the same template structure, but with hierarchical input data, you can use recursion. Examples are components like tree views for displaying folder structure, comments on your website, nested menus...everything where the parent and the child have the same structure.

Ok, let us build an example to show all of this in practice.

The Problem

Imagine this, you came to work like any other day, made yourself a nice cup of coffee and started reading your favorite blog. Suddenly, your boss comes and says that you need to implement a new page where you will display all folders, subfolders, and files without knowing how many of them will exist. It can show 10, 5 or 100 folders. You start scratching your head thinking about how to solve this, and of course, you think of recursion.

Minimal number of components to solve this is 1, but in our example, we will create two of them:

  • The root component
  • The folder component

But first, we need to create sample data.

The Data

As mentioned earlier, a recursion comes in handy when we have hierarchically organized data where the child has the same structure as its parent. Here is the data reflecting this:

const root = {
  text: 'Root Folder',
  leaf: false,
  expanded: true,
  children: [{
    text: 'Sub Folder 1',
    leaf: false,
    expanded: false,
    children: [{
      text: 'Sub Sub Folder 1',
      leaf: false,
      expanded: false,
      children: [{
        text: 'SomeFile1.js',
        leaf: true
      }]
    }, {
      text: 'Sub Sub Folder 2',
      leaf: false,
      expanded: false,
      children: []
    }, {
      text: 'SomeFile.txt',
      leaf: true
    }]
  }]
}

By having the data above, we are ready to create our components.

The Root Component

This component will be the starting point of our folder tree. It will initiate the rendering of all children, but it can also display some independent information if required since it will not be a part of the recursion itself.

It will contain one property, called folder for example, to which we will bind our root data object. This property will be passed on to the child component, which will recursively create the folder tree structure based on it.

Template

<template>
  <ul class="folders">
    <li>Folders</li>
    <folder v-bind:folder="folder"></folder>
  </ul>
</template>

The Code

import Folder from './Folder.vue';

export default {
  name: 'root',
  props: {
    folder: Object
  },
  components: {
    Folder
  }
};

The Styling

ul.folders {
  padding: 1rem;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  list-style: none
}
ul.folders > li:first-child {
  padding: 1rem 1rem 1rem 0
}

It's as simple as that.

The Folder component

This component is responsible for rendering each folder in our tree. It will display the information about the current folder and render its children if any. Also, the folders are clickable, and by clicking on one, the component will display its subfolders and files.

Template

<template>
  <li class="folder" v-bind:class="[folder.leaf ? 'is-leaf' : 'is-folder']">
    <span v-on:click="expand()">{{ folder.text }}</span>

    <ul class="sub-folders" v-if="folder.children && folder.children.length > 0" v-show="folder.expanded">
      <folder v-for="child in folder.children" v-bind:folder="child"></folder>
    </ul>
    <div class="folder-empty" v-else v-show="!folder.leaf && folder.expanded">No Data</div>
  </li>
</template>

The Code

export default {
  name: "folder",
  props: {
    folder: Object
  },
  methods: {
    expand() {
      if (this.folder.leaf) {
        return;
      }

      this.folder.expanded = !this.folder.expanded;
    }
  }
};

The Styling

li.is-folder {
  padding: 1rem;
  border-left: 1px solid #d3d3d3;
  margin-bottom: 0.5rem
}
li.is-folder > span {
  padding: 0.5rem;
  border: 1px solid #d3d3d3;
  cursor: pointer;
  display:inline-block
}
li.is-leaf {
  padding: 0 0 0 1rem;
  color: #000;
}
ul.sub-folders {
  padding: 1rem 1rem 0 0;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  list-style: none
}
div.folder-empty {
  padding: 1rem 1rem 0 1rem;
  color: #000;
  opacity: 0.5
}

Example Usage

In order to use the component you've just created, all you need to do is to import the root component where this functionality is required and pass in the data structure. For example, on the page your boss requested. The App.vue component would look something like this:

Template

<template>
    <div class="vue-app">
        <root v-bind:folder="root"></root>
    </div>
</template>

The Code

import Root from './Root.vue';

export default {
  name: 'app',
  data: function () {
    return {
      root: {
        text: 'Root Folder',
        leaf: false,
        expanded: true,
        children: [{
          text: 'Sub Folder 1',
          leaf: false,
          expanded: false,
          children: [{
            text: 'Sub Sub Folder 1',
            leaf: false,
            expanded: false,
            children: [{
              text: 'SomeFile1.js',
              leaf: true
            }]
          }, {
            text: 'Sub Sub Folder 2',
            leaf: false,
            expanded: false,
            children: []
          }, {
            text: 'SomeFile.txt',
            leaf: true
          }]
         }]
        }
      }
    },
    components: {
      Root
    }
};

And that's it! Your page is ready to display as many folders as it receives.

Wrapping Up

Recursion is not that hard as it looks. It's simple execution of the same code block over and over again with different input parameters until it reaches the base case.

I hope this article will provide a better understanding of recursion and how to create a recursive component with Vue. If this is true, and you find this helpful, show me some love and buy me a coffee to keep me juiced up for more articles.

Thank you for reading!