Recursion for Nested, Tree-Structure Components in Vue
Recursion is something every developer must learn at one point or another. I had been taught in school to seek recursive solutions to certain problems, but coming up with that solution on my own — in the wild — felt like a signal that I was ready for professional web development.
During a contract gig last summer, I needed to create a sort of accordion interface for a client’s dashboard. Thanks to help from Alex Regan, I ended up with a recursive implementation. It seemed to be the most readable and extendible way to go about creating the UI. In the process, we learned about some more advanced features that Vue.js has to offer: custom render methods and slots. This particular solution is perfect for any kind of drill-down interface, like nested comments and displays where details remain hidden by a few clicks.
It should also be noted that an iterative solution can often lead to a more performant and readable solution, as described at various points in my coworker’s latest blog post.
This article isn’t about iterative vs. recursive solutions — please don’t hurt me.
Let’s get the important stuff out of the way…
Source Code: https://github.com/kylemh/recursive_vue_component
Live App: https://kylemh.com/recursive_vue_component/
Similar Article: https://vuejsdevelopers.com/2017/10/23/vue-js-tree-menu-recursive-components/
For my initial attempt to create what I’ll refer to as the “accordion display” (pictured above), I thought iteratively. The problem with this approach is two-fold.
- The component wouldn’t be very single-purpose, as I’d have to pass through a lot of data to subcomponents.
- This wouldn’t be very readable — I could see myself having to explain my thought process to every person that looked at my code.
The system would’ve essentially looked like:
data → Dashboard → Top of tree → Branch of tree → Leaves of tree
top of tree and
branch of tree would’ve looked identical in function (and would’ve had very similar code). Also, I’d have to repeat logic if another branch layer were added. To me, it seemed very terse and my DRY senses were tingling. Alex suggested that I consider using slots to eliminate some unnecessary code:
After implementing slots, the final level of recursion failed to render consistently. I realized that I’d need to create a custom render function to properly provide the scoped slots to the tail-end of the recursion — I’m not the first person to encounter this problem.
Finally, we settled on a termination function and conditionally rendering the leaf if the data received had no further children to display. The main drawback is that it requires the data to be shaped in a very specific manner. The benefits are that it works well with any number of nestings, and I think the code is much more palatable. The final product lies in the source code with the initial recursive component call happening in App.vue
If you’re dealing with a ton of data there’s a few ways to keep things performant:
- Paginate the “last child” data.
- Modularize your API calls to occur for just the data necessary and only when necessary. A relevant example would be to ask for subregions only when a region is clicked, and ensure that data only lists the data necessary to call the next API call for the subregion’s options to open.
- Be careful when caching the results of API requests — if we’re talking tens of thousands of objects in a single view, you may blow the call stack. If you’re dealing with enough data, you may want to prevent caching and leverage something like Redis to shorten the length of time between client and server.
- If your data is real-time, a paginated stream may be your only option.
- Some combination of all of the above strategies.
Thanks for reading! I look forward to comments and critique.