Solution to Design Challenge 007: Visualising a Tree in React with Recursion
Sometimes, the simplest solutions require the most exploration, but the beauty lies in the simplicity we find along the way.
When I started learning programming at university, recursion was one of those concepts that captured my attention. I remember how it seemed to go around in circles in my head. I got the idea of recursion in literature; for instance, there’s an old Chinese poem I learned as a kid:
Once upon a time,
there was a mountain,
in the mountain,
there was a temple,
in the temple,
there lived a monk.
The monk says,
once upon a time,
there was a mountain...
The idea of repetition within itself was captivating. However, when it came to code—particularly when I first encountered the famous factorial or Fibonacci functions—I was stuck wondering: How does the computer know to stop? Or more importantly, how do I write a recursive function that doesn’t confuse itself into an infinite loop?
Implement a Tree with a list of items
Take the simple example of rendering a tree in HTML. If I want to create a tree structure manually, it’s straightforward:
<ul>
<li>Milk</li>
<li>
Cheese
<ul>
<li>Blue cheese</li>
<li>Feta</li>
</ul>
</li>
</ul>
However, when we try to do the same thing in code, especially with React, it's very tempting to just repeat ourselves, mapping over children
again and again until we can’t tolerate the repetition anymore:
const Tree = ({ tree }) => {
return (
<ul>
<li>
<span>{tree.name}</span>
</li>
<ul>
{tree.children.map((node) => (
<li>
<span>{node.name}</span>
<ul>
{tree.children.map((node) => (
<li>
<span>{node.name}</span>
<ul>
{tree.children.map((node) => (
<li>
<span>{node.name}</span>
<ul>
{/* ... */}
</ul>
</li>
))}
</ul>
</li>
))}
</ul>
</li>
))}
</ul>
</ul>
);
};
Suddenly, from nowhere we notice a pattern emerging, which we can simplify by writing a recursive component - by extracting the repeat part into a function and calling it inside itself:
const TreeNode = ({ node }) => {
return (
<li>
<span>{node.name}</span>
{node.children && (
<ul>
{node.children.map((child) => (
<TreeNode node={child} />
))}
</ul>
)}
</li>
);
};
Since TreeNode
is just a node in the tree, we wrap it with an ul
at the top level (to add the outmost ul
as a wrapper - you might also need that node to do some additional styling for the tree:
const Tree = ({ tree }) => {
return (
<ul>
<TreeNode node={tree} />
</ul>
);
};
The final HTML structure it generates would be:
<ul>
<li>
<span>Food</span>
<ul>
<li>
<span>Milk</span>
</li>
<li>
<span>Cheese</span>
<ul>
<li>
<span>Blue cheese</span>
</li>
<li>
<span>Feta</span>
</li>
</ul>
</li>
</ul>
<!-- the rest of the tree -->
</li>
</ul>
With this recursive component, we no longer repeat ourselves. Instead, we use a concise and elegant piece of code that points to itself to express the idea of nesting.
This is the most common usage of using a list item in HTML to represent a tree of data, it’s kind of straightforward to see that pattern after you grasp the idea of recursion, but again it’s hard to see it beforehand without failing a few times to get it right (yes, even after many years of coding, the recursive isn’t as direct as normal linear flow of programming).
A Real-World Process Visualisation
Recently, I worked on a project to visualize a complex process—think of planning a multi-step activity like cooking. Some tasks can run in parallel (e.g., boiling water while chopping vegetables), while others have strict dependencies (you have to cut ingredients before you can cook them).
The design seemed complicated at first glance because the nesting levels were arbitrary, and the node layout required linking nodes together correctly.
However, on closer analysis, I realized that all nodes could be categorized into two types: a T-shaped node for normal branches and an L-shaped node for the last child in a branch. Implementing these two variations allowed me to create a reusable structure that works for any level of nesting.
Keep reading with a 7-day free trial
Subscribe to The Pragmatic Developer to keep reading this post and get 7 days of free access to the full post archives.