How to Make a Simple Recursive React Component Today

How to Make a Simple Recursive React Component Today

Introduction

Recursive thinking is a crucial skill for programmers. It helps you break up big problems into smaller ones. Additionally, the recursive solution is often easier to read than the iterative one. It exists in practically all languages. In this post, we'll go over recursion and how to make a recursive react component.

Recursion

Definition of Recursion according to Wikipedia.

In computer science, recursion is a method of solving a computational problem where the solution depends on solutions to smaller instances of the same problem.

Let me try to simplify this and explain in more understandable language.

A function calling itself until the base condition occurs is called a recursion.

A recursive function consists of two parts:

  1. A Recursive Call calling itself.
  2. One or more Base Case(s).

Let's try to grasp this better by building a recursive method to retrieve the factorial of an integer. The product of all integers between 1 and n is the factorial of a non-negative number, n. (both inclusive).

$$ factorial(n)=n∗(n−1)∗...∗1 $$

Recursive Call

A recursive call happens when a function calls itself, adding to the stack of recursive calls.

function factorial(n) {
  //recursive case
  return n * factorial(n - 1);
}

As you can see, the factorial function is the multiplication of an integer by the factorial of the number before it. The above code will invoke itself until it reaches an error state. 'RangeError: Maximum call stack size exceeded'. To avoid this, let's add a Base Case to our function.

Base Case

In this case, the function returns the actual value, putting recursion to an end.

function factorial(n){
    //base case
    if(n == 0 || n == 1){
        return 1;
    }
}

Recursive factorial function

Let's bring it all together and see how our code looks.

function factorial(n){
    //base case
    if(n == 0 || n == 1){
        return 1;
    //recursive case
    }else{
        return n * factorial(n-1);
    }
}
let n = 4;
answer = factorial(n)
console.log("The factorial of " + n + " is " + answer);

We will look at recursion's use in React components in the next section.

What is a React Recursive Component?

A React Component is simply a functions that return JSX. So React components may be recursive just like any other functions. To illustrate a recursive react component may look something like this

function RecursiveComponent({ data }) {
  const { singleData, recursiveData } = data;
  if (singleData)
    return (
      //base case
      <div> {singleData} </div>
    )
  return (
    //recursive call
    <RecursiveComponent data={recursiveData} />
  )
}

Let's understand this better by building a recursive folder tree structure.

Building a recursive folder explorer component

A folder explorer component should render files and folders. A folder consists of several files or directories, and so on. Consider the following points to build the component:

  • Recursive data structure.
  • Base case to render the file.
  • Recursive case to render children.
  • Adding local state and styling to the recursive component.

Recursive data structure

{
  "type": "",
  "name": "",
  "data": [
    {
      "type": "",
      "name": "",
      "data": [
        {
          "type": "",
          "name": "",
        },
        {
          "type": "",
          "name": "",
        },
      ]
    }
  ]
}

The above structure consists of three main properties

  • type - current node type (file / folder).
  • name - name of the current node.
  • data - array of sub-nodes consisting of file/folder.

For the current example, let's consider the below input to our component.

let directory = {
  "type": "folder",
  "name": "root",
  "data": [
    {
      "type": "folder",
      "name": "folder A",
      "data": [
        {
          "type": "folder",
          "name": "subfolder A-1",
          "data": [
            {
              "type": "file",
              "name": "file A-1-1.txt"
            }
          ]
        },
        {
          "type": "folder",
          "name": "subfolder A-2",
          "data": []
        },
        {
          "type": "file",
          "name": "file A-3.txt"
        },
        {
          "type": "file",
          "name": "file A-4.txt",
        }
      ]
    },
    {
      "type": "folder",
      "name": "folder B",
      "data": [
        {
          "type": "folder",
          "name": "subfolder B-1",
          "data": [
            {
              "type": "folder",
              "name": "subfolder B-1-1",
              "data": [
                {
                  "type": "file",
                  "name": "file B-1-1-1.txt"
                },
                {
                  "type": "file",
                  "name": "file B-1-1-2.txt"
                }

              ]
            }
          ]
        },
        {
          "type": "file",
          "name": "file B-1.txt"
        }
      ]
    },
    {
      "type": "file",
      "name": "file C",
    }
  ]
}

Base case to render file or folder

function RecursiveFileExplorerComponent({ node }) {
  return (
    <div>
      {node.type == "folder" ? "📁" : "📄"} {node.name}
    </div>
  );
}

The output is as following BaseComponent.png

Recursive case to render children

if a node type is a folder and its data has array items, the recursive case is executed.

{
  node.type == "folder" &&
    node.data &&
    node.data.length > 0 &&
    node.data.map((item) => {
      return <RecursiveFileExplorerComponent node={item} />;
    });
}

Combining base case and recursive call the code will look like this

function RecursiveFileExplorerComponent({ node }) {
  return (
    <div
      style={{
        marginLeft: "10px",
        paddingLeft: "10px",
        borderLeft: "0.5px solid gray",
      }}
    >
      {node.type == "folder" ? "📁" : "📄"} {node.name}
      {node.type == "folder" &&
        node.data &&
        node.data.length > 0 &&
        node.data.map((item) => {
          return <RecursiveFileExplorerComponent node={item} />;
        })}
    </div>
  );
}

This is how the UI should look right now Recursive.png

Adding local state and styling to recursive component.

Let us add a state and CSS to handle the open and close functionality of a folder. The final component is as follows:

import { useState } from "react";

function RecursiveFileExplorerComponent({ node }) {
  const [isFolderOpen, setFolderOpen] = useState(false);
  return (
    <div
      style={{
        marginLeft: "10px",
        paddingLeft: "10px",
      }}
    >
      <div
        style={{
          cursor: node.type == "folder" ? "pointer" : "default",
        }}
        onClick={() => setFolderOpen(!isFolderOpen)}
      >
        {node.type == "folder" ? (isFolderOpen ? "📂" : "📁") : "📄"}
        {node.name}
      </div>
      {isFolderOpen && (
        <div style={{ padding: "5px", borderLeft: "0.5px solid gray" }}>
          {node.type == "folder" &&
            node.data &&
            node.data.length > 0 &&
            node.data.map((item) => {
              return <RecursiveFileExplorerComponent node={item} />;
            })}
        </div>
      )}
    </div>
  );
}
export default RecursiveFileExplorerComponent;

The final output is as following Recursive.gif

Please find the code sandbox reference below

To keep things simple, I only showed how to render a component recursively. However, there is much more that you can do in terms of styling and functionality. Maintaining the state throughout the tree instead of locally may be one of the biggest challenges as you add more features. However, we'll let you handle such aspects since it will be unique to your use case or the application you're developing.

I hope you found this post helpful. Please let me know in the comments if you've created a recursive component and what obstacles you experienced.

I've only recently begun blogging, but I'll soon be adding more articles about coding and best practises. Subscribe to my newsletter to get notified when I publish new content.

Did you find this article valuable?

Support Supreet Sethi by becoming a sponsor. Any amount is appreciated!