Often times, particularly when dealing with complex nested data, React developers need to re-think the way they're structuring their component trees. In certain cases, when the data requires it, component trees can't be rendered in an iterative fashion, instead React developers must rely on recursion to display their data.
In this article, I’ll dive into how to build React components recursively, and some of the unique challenges that can arise when doing so.
This blog post is a written version of a talk I gave as part of our February 2019 meetup.
What is Recursion?
Recursion is a method of solving a problem where the solution depends on solutions to smaller instances of the same problem. Whenever I think of recursion, I often conjure up the image of Russian nesting dolls.
In a set of Russian nesting dolls, each doll is nested in another and they all look identical. This is often the case with recursive problems. They’re composed of a series of smaller and smaller problems each nested in the other, but the problems themselves are identical.
A good example of a problem with a recursive solution is the factorial function. When calculating a factorial, you take a number and multiply it by the factorial of that number minus one. This process continues until you reach the number 1, the base case.
If we wanted to write a factorial function, that function would need to call itself in order to calculate the factorial.
Let’s take a look at a factorial function in JavaScript and break down exactly what’s happening.
function factorial(n) {
// base case
if (n === 1) {
return 1;
}
// recursive call
return n * factorial(n - 1);
}
factorial(5); // 120
All recursive functions like the one above have two parts, the base case and the recursive call.
The recursive call is when the function calls itself. In the case of the factorial function, we’re returning the result of multiplying n by the result of calling factorial of n - 1.
The base case is the final step in the recursive chain, it’s where we’re returning an actual value instead of the result of another recursive function call. In the factorial function, the base case is when n is equal to 1.
This is a fairly standard recursive function. Functions like this are commonly used in software all around the world.
Recursion in React
One of the cool things about React is that React components are essentially functions which return JSX. Therefore, just like with any other functions, React components can be recursive.
function MyComponent({ prop1 }) {
return (
// base case
{prop1 !== 0 &&
// recursive call
return n * factorial(n - 1);
}
)
}
Above we have an example of a recursive React component. If you look closely, you’ll see that just like with the factorial function, we have a recursive call and a base case.
Here, the recursive call is when the component renders itself, passing in a modified version of the props it received. The base case is a conditional check to determine whether it should render itself again or stop.
This, in a nutshell is how recursion is done in the context of React components. Just like with the factorial function, special care needs to be taken with constructing the base case so you don’t end up in a situation where you’re recursing forever.
Now that we have an understanding of how it works, let’s take a look at a situation where recursion in React may be necessary.
Recursion Use Case
As with most things in a front end app, the use cases are determined by the data. Therefore, whether or not a recursive component is necessary will depend largely on the data you’re attempting to display.
A good example of a time when recursion is necessary is when dealing with a data set which is identically and arbitrarily nested.
Suppose we’re building a front end application for an online pizza ordering app. In the app, we want to allow users to select which toppings they want on their pizza.
Above we have a screenshot of this pizza ordering app. Toppings are selected using this nested checkbox component. When you select a topping, there may be more specific options to choose from in that same category.
In this case, when you select Chicken, you can also choose between Buffalo and BBQ chicken, and in Buffalo chicken you can select Mild or Hot, and the type of pepper.
Depending on the toppings structure, we will have an arbitrary number of options for each topping, and we want to be able to display this in React.
To understand how this will work with recursion, let’s take a look at the data that we might get back from the server about these toppings.
[
{
name: 'Pepperoni',
id: 'pepperoni-id',
subOptions: [
{
name: 'Spicy',
id: 'spicy-id',
subOptions: [],
},
],
},
{
name: 'Chicken',
id: 'chicken-id',
subOptions: [
{
name: 'Buffalo',
id: 'buffalo-id',
subOptions: [
{
name: 'Mild',
id: 'mild-id',
subOptions: [],
},
...
];
This data is organized in a nested structure. Each topping option has three fields: name, id, and subOptions. The name and id fields are self explanatory, but the subOptions field is more complex. The subOptions field is an array of topping options each identical to their parent option, which represent more specific topping choices.
We can represent the toppings data in this structure because, thanks to the subOptions field, the topping choices can be arbitrarily nested.
Challenges with Recursion in React
Suppose we wanted to implement this toppings component in React. Because the toppings data is arbitrarily nested, and we won’t know exactly how many nested toppings we need to display up front, we’ll want to use a recursive component.
In order to implement this recursively, we’ll need to keep a few things in mind. Specifically, there are three distinct challenges we will face in implementing the component.
The first challenge is managing the component’s state. As the user goes through and selects their toppings, and specifically as they select further and further nested sub-options, we’ll need a way of keeping track of what they’re selecting. This is generally a straightforward process if we’re not using a recursive component, but in a situation where the component is going to be rendering itself an arbitrary number of times, it could easily become difficult to keep track of the state.
The second challenge is notifying a parent component of changes to its child components. When we render components recursively, a component's children will be instances of itself. Therefore, when designing props and callbacks, we’ll need to keep in mind the fact that the component will essentially be interacting with a copy of itself.
Finally, the third challenge is keeping styling consistent. We’ll want the component to look good and act responsively even if it’s rendering itself. Therefore, styles must be set up in such a way where we don’t run into any nesting problems.
React Implementation
While there are several ways you could implement this component in React, it’s clear that this problem lends itself really well to a recursive solution.
I’ve implemented this component in a recursive way in the following CodePen:
See the Pen Recursive React Presentation by Mike (@mikedane94) on CodePen.
In the provided video I’ll walk you through exactly how this works, how the above mentioned challenges were addressed, and give you an idea of how similar component can be implemented.
Next Steps
Using React in your application? Join our Slack and chat with us in the #react channel!
Looking for React consulting? We can help! Book a free consultation and we will look at your app, ask questions, evaluate potential issues, spend some time auditing it, and provide you with a report of our recommendations—at no cost.
Previous Post