Recursive paths with React Router V4
Tags: react, javascript, reactrouter
Published: 2019-01-03 17:21:11

---
title: 'Recursive paths with React Router V4'
description: "Because React Router is just components, you can do crazy things like having recursive routes. In this post we'll learn how they work by breaking down the 'Recursive Paths' example on the React Router docs."
tags: "react, javascript, react-router"
cover_image: "https://tylermcginnis.com/static/3f92f4013e51f80c98de3ba38b98f68f/f20d7/recursive-paths-with-react-router.webp"
canonical_url: "https://tylermcginnis.com/react-router-recursive-paths/"
published: "true"
---

Because React Router is just components, you can do crazy things like having recursive routes. In this post we'll learn how they work by breaking down the 'Recursive Paths' example on the React Router docs.

{% youtube gb74jqcPN_Q %}

Recursive routes aren't the most pragmatic thing in the world, but they really show off the benefits of React Router's component based approach to routing.

The main idea here is that since React Router is just components, theoretically, you can create recursive and therefor infinite routes. The secret lies in setting up the right data structure which can lead to infinite routes. In this example we'll use an array of people who all have an id, a name, and an array of friends.

```js
const users = [
  { id: 0, name: 'Michelle', friends: [ 1, 2, 3 ] },
  { id: 1, name: 'Sean', friends: [ 0, 3 ] },
  { id: 2, name: 'Kim', friends: [ 0, 1, 3 ], },
  { id: 3, name: 'David', friends: [ 1, 2 ] }
]
```

By having this data structure set up this way, when we render a `Person`, we'll render all of their friends as `Link`s. Then, when a `Link` is clicked, we'll render all of that person's friends as `Link`s, and on and on. Each time a `Link` is clicked, the app's pathname will become progressively longer.

Initially, we'll be at `/` and the UI will look like this

```js
Michelle's Friends

  * Sean
  * Kim
  * David
```

If `Kim` is clicked, then the URL will change to `/2` (Kim's ID) and the UI will look like this

```js
Michelle's Friends

  * Sean
  * Kim
  * David

Kim's Friends

  * Michelle
  * Sean
  * David
```

If `David` is clicked, then the URL will change to `/2/3` (Kim's id then David's id) and the UI will look like this

```js
Michelle's Friends

  * Sean
  * Kim
  * David

Kim's Friends

  * Michelle
  * Sean
  * David

David's Friends

  * Sean
  * Kim
```

And this process repeats for as long as the user wants to click on `Link`s.

Once you have the right data structure set up, the next important step is to continually render a `Route` and some `Links`s. Because we're creating infinite routes, we'll need to make sure we have a `Route` that is rendered every time a `Link` is clicked. If not, we won't get any more matches which means React Router won't render any more components.  In both our `Link` and our `Route` we'll need to know the app's current pathname so that we can append to it every time a `Link` is clicked (like in the example above, we went from `/2` to `/2/3`, and on). Luckily for us, React Router gives us the pathname with `match.url`. With that in mind, the initial part of our `Link` will look like this

```js
<Link to={`{match.url}/${id}}>
```

and the `Route` we render will match on a similar pattern then render the same component.

```js
<Route path={`${match.url}/:id`} component={Person}/>
```

Now that we have the basics down, let's start building out the component which is going to be recursively rendered, `Person`.

Remember, there's a few things this component needs to be responsible for.

1) It should render a Link component for every one of that specific person's friends.
2) It should render a Route component which will match for the current pathname + /:id.

As with all recursive problems, we need to somehow "kick off" the recursion. Typically this involves invoking the function but if it's a component that's being called recursively, we can do that by simply creating the element.

```js
import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'

const users = [
  { id: 0, name: 'Michelle', friends: [ 1, 2, 3 ] },
  { id: 1, name: 'Sean', friends: [ 0, 3 ] },
  { id: 2, name: 'Kim', friends: [ 0, 1, 3 ], },
  { id: 3, name: 'David', friends: [ 1, 2 ] }
]

const Person = ({ match }) => {
  return (
    <div>
      PERSON
    </div>
  )
}

class App extends React.Component {
  render() {
    return (
      <Router>
        <Person />
      </Router>
    )
  }
}

export default App
```

Now what we need to do is figure out how to get the specific friend's information from our `users` array so we can grab their name and render their friends. You may notice a problem here. Eventually `Person` is going to be rendered by React Router so it'll be passed a `match` prop. It's this `match` prop we'll use to get the current pathname and (with help from `users`) the person's name and friends list. The problem is we're rendering `Person` manually inside the main `App` component to kick off the recursion. That means `match` is going to be undefined the first time `Person` is rendered. The solution to this problem is simpler than it may seem. When we first manually render `<Person />` we'll need to pass it a `match` prop just as React Router would.

```js
class App extends React.Component {
  render() {
    return (
      <Router>
        <Person match={{ params: { id: 0 }, url: '' }}/>
      </Router>
    )
  }
}
```

Now, everytime `Person` is rendered, including the first time, it'll be passed a `match` prop which will contain two things we need, `url` for rendering our `Route` and `Link`s and `params.id` so we can figure out which person is being rendered.

Alright back to the main goal at hand. `Person` needs to

1) It should render a Link component for every one of that specific person's friends.
2) It should render a Route component which will match for the current pathname + /:id.

Let's tackle #1. Before we can render any `Link`s, we need to get the person's friends. We already know the person's `id` from `match.params.id`. Using that knowledge with the `Array.find` method means getting the friends info should be pretty straight forward. We'll create a helper function for it.

```js
const users = [
  { id: 0, name: 'Michelle', friends: [ 1, 2, 3 ] },
  { id: 1, name: 'Sean', friends: [ 0, 3 ] },
  { id: 2, name: 'Kim', friends: [ 0, 1, 3 ], },
  { id: 3, name: 'David', friends: [ 1, 2 ] }
]

const find = (id) => users.find(p => p.id == id)

const Person = ({ match }) => {
  const person = find(match.params.id)

  return (
    <div>
      PERSON
    </div>
  )
}
```

Slowly getting there. Now we have the person, let's render some UI including the `Link` for each of their friends.

```js
const users = [
  { id: 0, name: 'Michelle', friends: [ 1, 2, 3 ] },
  { id: 1, name: 'Sean', friends: [ 0, 3 ] },
  { id: 2, name: 'Kim', friends: [ 0, 1, 3 ], },
  { id: 3, name: 'David', friends: [ 1, 2 ] }
]

const find = (id) => users.find(p => p.id == id)

const Person = ({ match }) => {
  const person = find(match.params.id)

  return (
    <div>
      <h3>{person.name}’s Friends</h3>
      <ul>
        {person.friends.map((id) => (
          <li key={id}>
            <Link to={`${match.url}/${id}`}>
              {find(id).name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  )
}
```

We're so close to being done. Now that we have a `Link` for each of the person's friends, as mentioned in #2, we need to make sure we also render a `Route`.

```js
const Person = ({ match }) => {
  const person = find(match.params.id)

  return (
    <div>
      <h3>{person.name}’s Friends</h3>
      <ul>
        {person.friends.map((id) => (
          <li key={id}>
            <Link to={`${match.url}/${id}`}>
              {find(id).name}
            </Link>
          </li>
        ))}
      </ul>
      <Route path={`${match.url}/:id`} component={Person}/>
    </div>
  )
}
```

The full code now looks like this

```js
import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'

const find = (id) => users.find(p => p.id == id)

const users = [
  { id: 0, name: 'Michelle', friends: [ 1, 2, 3 ] },
  { id: 1, name: 'Sean', friends: [ 0, 3 ] },
  { id: 2, name: 'Kim', friends: [ 0, 1, 3 ], },
  { id: 3, name: 'David', friends: [ 1, 2 ] }
]

const Person = ({ match }) => {
  const person = find(match.params.id)

  return (
    <div>
      <h3>{person.name}’s Friends</h3>
      <ul>
        {person.friends.map((id) => (
          <li key={id}>
            <Link to={`${match.url}/${id}`}>
              {find(id).name}
            </Link>
          </li>
        ))}
      </ul>
      <Route path={`${match.url}/:id`} component={Person}/>
    </div>
  )
}

class App extends React.Component {
  render() {
    return (
      <Router>
        <Person match={{ params: { id: 0 }, url: '' }}/>
      </Router>
    )
  }
}

export default App
```

The first time `Person` is rendered, we pass it a mock `match` object. Then, `Person` renders a list of `Link`s as well as a `Route` matching any of those `Link`s. When a `Link` is clicked, the `Route` matches which renders another `Person` component which renders a list of `Link`s and a new `Route`. This process continues theoretically forever as long as the user continues to click on any `Link`s.


***

This was originally published at [TylerMcGinnis.com](https://tylermcginnis.com/react-router-recursive-paths/) and is part of their [React Router](https://tylermcginnis.com/courses/react-router/) course.