Nyior Clement I'm an all around software engineer who codes, writes, and sometimes designs. If you want to talk Python, I'm your guy. I own this poky corner of the interwebs xD.

Vue recursive components: Rendering nested comments

6 min read 1744

Vue Recursive Comment Section

Most modern social networks include a feature where users can reply to a comment by commenting on that specific comment. If we visualize that, the data for our comments would look like the structure below:

- Comment A
                - comment a1
                                - comment a12
                - comment a2
- Comment B
- Comment C

Comment A has sub-comments comment a1 and comment a2. In turn, comment a1 has sub-comment comment a12, which could also have its own sub-comments.

With this structure, we could have a comment with an infinite number of layers of sub-comments. You’re probably already familiar with this method of structuring data, which is known as a tree. For example, think of the directories on your PC where a folder could have sub-folders and so on.

In this article, we’ll explore how we can use recursive components in Vue to manage tree-like structured data. It’s hard to talk about Vue recursive components without talking about recursion, so let’s review the basics first before exploring what a recursive component is and learning how to create one in Vue.

Recursion: A quick introduction

In its simplest form, recursion is the term we use to refer to a function that calls itself. For example, consider the function below:

function sum_numbers(arr, n) {
        return sum_numbers(arr, n - 1) + arr[n - 1];
}

Although it is faulty, the function above could be considered recursive because it references itself within its body. However, this definition is not all-encompassing. Recursion is an approach to problem-solving. It is based on the premise that given a problem, we could find its solution if we know the solution to its sub-problem.

For example, the sum_numbers function above finds the sum of all the numbers in a given array, arr = [1, 2, 3, 4, 5]. In the sum problem, if we know the sum of all the numbers before five, then we can reduce our problem to the sum of numbers in arr equals the sum of all the numbers before the last element and the last element.

The expression return sum_numbers(arr, n - 1) + arr[n - 1]; in the sum_numbers function we defined above does exactly what we just described .

To picture how our function will execute from beginning to the end given the input, [1, 2, 3, 4], consider the code below:

**sum_numbers([1, 2, 3, 4], 4)
    |
        calls
                |**
**sum_numbers([1, 2, 3], 3) + 4
    |
        calls
                |
sum_numbers([1, 2], 2) + 3
                |
        calls
                |
sum_numbers([1], 1) + 2
                |
        calls
                |
sum_numbers([], 0) + 1 --** WE HAVE A PROBLEM HERE

You may notice that we still have a problem; our recursive function tries to add an empty list to a number. In fact, the bigger problem is that our recursive function will keep calling itself infinitely.



To ensure that our recursive function does not execute to infinity, we need a base case. You can think of the base case as the point at which we want our function to stop calling itself. In our case, the sum_numbers function should stop calling itself if it has just one number in it. If the array has just one number left, then there’s nothing to multiply that number with, in which case we just return the number.

With that knowledge, we can now modify our function to have a base case as shown below:

// where n is the length of arr
function sum_numbers(arr, n) {
        if(n <= 1){ //Base Case
                return arr[0];
        }else{
                return sum_numbers(arr, n - 1) + arr[n - 1];
        }
}

That’s fundamentally what recursion is about, but what’s the connection to Vue recursive components?

Vue recursive component: An overview

Remember, components in Vue are reusable Vue instances. Most of the time, when we create a component in Vue, it’s just so we can reuse it in other places. For example, think of an ecommerce website where you could have products displayed on multiple pages. You could have just one Product Component that you could render on different pages as opposed to repeating the code for the Product Component on every page it’s needed.

A Vue component is considered recursive if it references itself within its own template. Recall that we mentioned that components are designed to be reused in other components. Recursive components differ from regular components in this regard. In addition to being reused in other places, recursive components also reference themselves within their templates.

Why would a component reference itself? When you render a component in some other component, the guest component is the child, and the component within which it is rendered is the parent.

In our earlier Product Component example, that component could have ProductReview as its child component. In this case, it makes sense that we have two different components for the entities those components represent because Product and Reviews are different in every respect.


More great articles from LogRocket:


But, if we take the case of a Comment and Sub-comment, then the story changes. Both components represent the same thing. A sub-comment is also a comment. Therefore, it doesn’t make sense that we have two different components for Comment and Sub-comment because they are structurally the same. We could just have one Comment component that references itself. Still too abstract? See the snippet below:

<template>
  <li class="comment">
    <span>{{ comment.comment }}</span>
    <comment v-for="reply in comment.replies" :comment="reply"></comment>
  </li>
</template&gt;

<script>
export default {
  name: "comment",
  props: {
    comment: Object
  }
};
</script>

Although it is buggy, the component above could be considered recursive because it references itself. Like recursive functions, a recursive component too must have a base case, which is missing in the code above. One other important thing to note here is that for a component to be able to reference itself, the name option must be defined. In our case, ours is name: "comment".

Now that we understand what recursive components are in Vue, let’s see how we could use them to build a nested comments interface.

Building the comments interface

Prerequisites

  • Knowledge of JavaScript
  • Experience with Vue
  • Node.js installed locally
  • Vue and the Vue CLI installed
  • Any IDE of your choice; I’ll be using VS Code

Setting up the Vue development environment

First, we’ll initialize a new Vue project. Launch your system’s terminal window and navigate to any directory you want to create your Vue project in. Run the command vue create nested-comments in your terminal. nested-comments is our project’s name.

You’ll then be prompted to add configuration. Go ahead and select the defaults. Once you’re done, a project with the structure shown in the image below will be generated for you:

Vue Nested Comments Folder Structure

Run the vue serve command in your project’s root directory to fire up Vue’s development server and test things out.

Rendering the nested comments with the recursive component

To render our nested comments to the DOM, first, delete all the files in src/views and src/components. Then, create src/components/Comment.vue. The Comment.vue component would house our recursive component code.

Copy and paste the code below in your Comment.vue component:

<script>
        export default {
          name: "recursive-comment",
          props: {
            comment: {
              type: String,
              required: true,
            },
            replies: {
              type: Array,
              default: () => [],
            },
          },
        };
</script>

In the code snippet above, we’ve named our component recursive-component. Remember, in Vue, a recursive component must have a name declared. Furthermore, our component would expect the props comment and replies to be passed to it anywhere it’s referenced.

Now that we have the script section of our component set up, copy and paste the code snippet below in your component directly above the script section:

&lt;template>
  <li>
    <span class="comment">{{ comment }}</span>

    <ul class="replies" v-if="replies.length">
      <div v-for="(item, index) in replies" :key="index">
        <recursive-comment
          v-bind="{
            comment: item.comment,
            replies: item.replies,
          }"
        />
      </div>
    </ul>
  </li>
</template>

The code snippet above sets up the template section of our component. The recursive-comment component references itself within its own template. Fundamentally, this makes our Comment component recursive. v-if="replies.length" is our base case. Once that condition evaluates to false, the recursive call would terminate.

Next, we just need to render our component in App.vue. To do so, override the content of src/App.vue in your project with the code snippet below:

<template>
  <ul v-for="(item, index) in comments" :key="index" class="comments">
    <Comment
      v-bind="{
        comment: item.comment,
        replies: item.replies,
      }"
    />
  </ul>
</template>

<script>
import Comment from "@/components/Comment";

export default {
  data: () => ({
    comments: [
      {
        comment: "First comment",
        replies: [
          {
            comment: "sub-comment 1 for comment 1",
            replies: [
              {
                comment: "sub-sub-comment 1",
                replies: [
                  {
                    comment: "sub-sub-sub-comment 1",
                  },
                  { comment: "sub-sub-sub-comment 2" },
                ],
              },
              { comment: "sub-sub-comment 2" },
            ],
          },
          { comment: "sub-comment 2 for comment 1" },
        ],
      },

      {
        comment: "Second comment",
        replies: [
          {
            comment: "sub-comment 1 for comment 2",
            replies: [
              { comment: "sub-sub-comment 1" },
              { comment: "sub-sub-comment 2" },
            ],
          },
          { comment: "sub-comment 2 for comment 2" },
        ],
      },
    ],
  }),

  components: {
    Comment,
  },
};
</script>

<style>
.comments ul {
  padding-left: 16px;
  margin: 6px 0;
}
</style>

In the code snippet above, we’ve initialized our list of comments with some dummy data, but ideally, you’d be pulling this data from your database. We then rendered our Comment.vue in the App.vue template, passing each component to it as prop.

Save your changes and run your server with vue serve. Finally, point your browser to http://localhost:8080. You should see the interface below:

Nested Comments Dummy Data Display

Conclusion

Although our example isn’t what a typical comment section would look like, our goal was to explore how we could leverage the power of recursive components in Vue to render nested data, like comments, to the DOM.

We saw how we could do that by creating a component that references itself within its own template. This recursive approach is especially useful when rendering data entities that may seem different but are structurally the same. For example, take the case of our comments and replies.

At first glance, it would appear that we’d need two components, one for comments and the other for replies. But, with the recursive approach, we were able to render both with one component. Most importantly, our component would render all comments and replies until it hits the base case. I hope you enjoyed this article, and be sure to leave a comment if you have any questions.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Nyior Clement I'm an all around software engineer who codes, writes, and sometimes designs. If you want to talk Python, I'm your guy. I own this poky corner of the interwebs xD.

Leave a Reply