February 20, 2020

Recursive React Components

It can be easy to forget that React components are just functions. They receive inputs, they give us an output, and they might trigger some side effects in the process. Because they are simply functions, we can use patterns with them that we use with other functions. Like recursion.

What is recursion?

Recursion is the pattern of a function calling itself. A very common example of a recursive function is calculating a factorial of a number. Consider the following function:

function factorial(number) {
  // This is the base case. I'll explain this shortly
  if (number <= 1) {
    return 1
  }

  return number * factorial(number - 1)
}

Our function receives a number to factorialize, but rather than use an iterative loop, such as a for or while loop, we take advantage of the fact that we can calculate the final result by utilizing the results of calling factorial on smaller numbers. That is, we recognize the pattern:

factorial(4) === 4 * factorial(3)
factorial(3) === 3 * factorial(2)
factorial(2) === 2 * factorial(1)
factorial(1) === 1

If we take this list of equal values and reverse the order of the functions we see called, we can derive the function call stack of factorial(4).

factorial(4) leads to factorial(3) getting called, which leads to factorial(2) getting called, which leads to factorial(1) getting called, which returns the value of 1, which is fed back into factorial(2) which returns the value of 2, which is fed back into factorial(3) which returns the value of 6, which is fed back into factorial(4), which returns the value of 24.

Easy, right?!

Base Cases

Consider for a moment if we commented out the following bit of code from our factorial function:

function factorial(number) {
  // if (number <= 1) {
  //   return 1
  // }

  return number * factorial(number - 1)
}

What would happen? Well, we'd never get an answer. We'd never get a call to factorial that returned a value. We would continue to add factorial calls to the call stack until we exceeded the maximum number of calls allowed on the stack. We would have a "stack overflow" (and yes, that is where the name comes from).

Every recursive function needs to have a base case that ends the recursion. In the case of factorial, it's when we have a number less than or equal to 1 passed in for number. This base case ends the chain of recursive calls, and starts the process of returning values up the stack to get the final result.

Recursion applied to components

Now that we understand recursive functions, let's apply this knowledge to components. A recursive component is a component that renders itself as one of its children. Let me give you an example.

Consider a directory of folders. It might look something like this:

src
  |- css
  |- js
    |- utils
    |- components
public
  |- images

How can we represent this as data to pass to a recursive component? Pretty simply actually, we can use a tree. A tree is a graph data structure where each child node has a single parent. For example, the HTML structure of a website is a tree. No element on a page has more than one parent (all they way up to the <html> tag). We can make a tree structure for our folders like this:

const folders = [
  { name: 'src', children: [
    { name: 'css', children: [] }
    { name: 'js', children: [
      { name: 'utils', children: [] },
      { name: 'components', children: [] }
    ]}
  ]},
  { name: 'public', children: [
    { name: 'images', children: [] }
  ]}
]

We have a top level of folders, and folders nested up those are on a children property. Now that we have our data, how do we make use of it in a recursive component?

Similar to how we recognized that factorial can be solved by using the result of factorial called on smaller numbers, we can recognize that a tree is the result of combining smaller trees. Understanding this, we can make a Tree component that will call itself to render smaller Trees.

function Tree({ items }) {
  // our base case, if we have no items, render nothing.
  if (!items || !items.length) {
    return null
  }

  return items.map(item => (
    <React.Fragment key={item.name}>
      <div>{item.name}</div>
      {/* And here's the recursion! */}
      <Tree items={item.children} />
    </React.Fragment>
  ))
}

If we were to pass the folder data we created earlier to the component, that will look like this:

src
css
js
utils
components
public
images

Wait! That's not what we want to represent. That's just a flat list. What happened?

Well, we forgot to style it. Let's track the depth of the folder with each level and update the padding-left CSS property accordingly.

// We add a `depth` property with a default value of 0
// That way we don't have to pass it to the top level of the tree
function Tree({ items, depth = 0 }) {
  if (!items || !items.length) {
    return null
  }

  return items.map(item => (
    <React.Fragment key={item.name}>
      {/* Multiply the depth by a constant to create consistent spacing */}
      <div style={{ paddingLeft: depth * 15 }}>{item.name}</div>
      <Tree items={item.children} depth={depth + 1} />
    </React.Fragment>
  ))
}

Notice how simple it is to increment the depth prop with recursion. Each layer deeper we go in the tree, the value is increased by 1. Let's take a look at our recursive component now:

src
css
js
utils
components
public
images

That looks a bit more like the folder structure we all know and love. Let's add a few more touches to make it a bit nicer.

src
css
js
utils
components
public
images

That's starting to look like something you could use in a real UI. A little interactivity, displaying of files, and we'd really have something.

Conclusion

I encourage you to get comfortable with recursion. It doesn't have to be scary. Often, it's the simplest and most elegant solution to a problem. Who knows, it might even help you pass a job interview one day. Don't be afraid to experiment with it in your React work, you may discover some interesting UIs because of it.

Categories
JavaScriptSoftware EngineeringWeb Development

+0

Liked the post? Why not show it?! Stroke Kyle's ego by stroking clicking his beard. You can click up to 50 times if you really liked it.


Spot a typo? Join the contributors below and submit a PR with the fix! This entire blog is open sourced at https://github.com/kyleshevlin/blog
alexloudenjacobwsmithbryndymentJacobMGEvanseclectic-codingjhsukgcreativeerikvorhesHaroenvmarktnoonan
Newer Post: Managing Cyclomatic Complexity
Kyle Shevlin's face, which is mostly a beard with eyes
Kyle Shevlin is a front end web developer and software engineer who specializes in JavaScript and React.