Recursive Components in Vue.js!
Using Stop Conditions, Moving Data Upwards and Downwards, Reactive Id Properties
Yes, you heard that right! Today, we will talk about recursively rendering front end components. Because in Vue.js that is a thing!
Recently, while developing a tree style component, I found myself wanting to recursively render a node component. Just so I could create my entire tree of one node. Initially, I had my doubts that this could even be done. Because well, I had never needed to. Anyways let’s get into it.
Breaking Down Recursion! …, Breaking Down Recursion! … , Breaking Down Recursion! …
Before continuing, I wanted to do a quick breakdown of recursion just so, we’re on the same page. Here’s the definition:
Recursion is a method of solving a problem where the solution depends on solutions to smaller instances of the same problem. Such problems can generally be solved by iteration, but this needs to identify and index the smaller instances at programming time.Recursion solves such recursive problems by using functions that call themselves from within their own code.
Recursion (computer science), Wikipedia
So, essentially recursion, is a type of looping in which a function (in our case a component) will call itself from within itself. This diagram might help paint a better picture.
Creating Recursive Components in Vue
Alright, now we will apply this concept to Vuejs. First thing to note is that the syntax for injecting a component in itself, is the exact same as injecting any other component.
However, while this is valid syntax, the code as is, produces an Uncaught Range Error for overloading the call stack. The main risk of anything to do with recursion is it’s very easy to unknowingly get caught in an infinite recursion. Since, the node component referenced itself, Vue will attempt to render a child instance of node for every instance of node there is. Therefore infinite loop.
Using A Stop Condition
A stop condition is the only way we can prevent an infinite loop. That is a condition which when reached will break the code out of its recursion. Checkout my example below.
Notice the v-if directive where the child component is placed in the template. My stop condition is to render only if the data node has child data. Place you’re stop condition in the v-if. Don’t use v-show! Vue will still attempt the endless render and you’ll get that call stack error. Lastly, make sure your logic for the condition is sound.
Now that our component has been built safely, we will need to address how to transfer data between each instance. From root all the way down to the (n)th most child each instance needs access to certain data. The process between moving data up and down are a bit different.
Downwards Data Flow to Recursive Instances:
For downward data flow, we can using props to propagate data to child instances. Props allow parent components to instantiate its direct children components with data. Because of recursive nature of this component, when we prop data from parent to the child node component. Automatically, the child instance of this component will do the same to its child component. Creating a chain linkage of data. Root will pass data to Child1. Child1 will pass that along to Grandchild1, and so on and so on.
Keep in mind that props operate in a one-way flow. Changes made to a prop within a child instance will not propagate up to parent components. Leading me to the next topic, upwards data flow.
Upwards Data Flow (Grandchild to Grandparent):
By upwards data flow, I’m referring to the communication for data from a child instance back upwards to its parent. Only we’ll have to create a chain, so that an (n)th child can talk all the way up back up to the root. Like I said before, props only flow one way, which means we us a different method of communicating. For, this we will make use of the built-in Event API. Using an $emit directive sends data pack up to its direct parent component. The $on directive is used a listener for events of children components. Here is how to do this.
Firstly, decide on an initial trigger for which to emit an event. This trigger could be from user action like a click on an element. Now, each node instance can start the initial trigger, however this is no good if they can’t listen for data below. Place a v-on directive on the child and listen for that event on the child component, once received the next action should be to emit data upwards passing in the data, it received. This again creates a chain propagating data towards the top.
Bonus Tip: (How To Create Unique Id’s For Elements In A Recursive Component):
Lastly, a bonus tip for you. When marking an element with the id property, make sure that that identifier is distinguishable from other the elements within other recursive component instances. Example, since Vue will been rendering the same elements multiple times a nonreactive string will be the same for each instance. Get around this by using a compound identifier. Prefix an id with a generic descriptor like title and then append the id of the instances data. Something like: “<div id=”’node-’ + data.id” />. Now you can easily, query an element from every instance.
Thank you, for reading this far, and supporting this article. If you liked it, give it a clap, and if you have questions/comments feel free to post.
Also, if you would like to see everything today put into practice. Checkout my recursive file tree on bit.dev! It’s live and interactive.