clubmate.fi

A good[ish] website

Web development blog, loads of UI and JavaScript topics

Render a nested list with a recursive React component

Filed under: Markup— Tagged with: React

When rendering deep lists, recursion is pretty much the only right way to do it. Recursive React components work like a charm.

Deep array

Take the following shape, a deep array that renders a table of contents, this specifically is taken from the style guide post:

const toc = {
  items: [
    {
      url: '#this-is-an-h2-heading',
      title: 'This is an h2 heading',
      items: [
        {
          url: '#heres-an-h3',
          title: 'Here’s an h3',
          items: [
            {
              url: '#h4-deep-enough',
              title: 'H4, deep enough?',
              items: [
                {
                  url: '#and-an-h5',
                  title: 'And an h5 😬'
                }
              ]
            }
          ]
        }
      ]
    },
    {
      url: '#lists',
      title: 'Lists',
      items: [
        {
          url: '#unordered-list',
          title: 'Unordered list'
        },
        {
          url: '#ordered-list',
          title: 'Ordered list'
        }
      ]
    },
    {
      url: '#code',
      title: 'Code',
      items: [
        {
          url: '#inline-code',
          title: 'Inline code'
        },
        {
          url: '#sample-output-samp',
          title: 'Sample output, samp'
        }
      ]
    },
    {
      url: '#embeds',
      title: 'Embeds'
    },
    {
      url: '#media',
      title: 'Media'
    },
    {
      url: '#html-elements',
      title: 'HTML elements',
      items: [
        {
          url: '#address',
          title: 'Address'
        },
        {
          url: '#superscript-and-subscript',
          title: 'Superscript and subscript'
        }
      ]
    },
    {
      url: '#boxes',
      title: 'Boxes'
    }
  ]
}

See how some of the elements contain items and again items... a copy of itself in a way, a fractal-like structure. Whenever there’s fractals, there’s recursion.

The markup for the list should look something like this:

<ul>
  <li>
    <a href="#this-is-an-h2-heading">This is an h2 heading</a>
    <ul>
      <li>
        <a href="#heres-an-h3">Here’s an h3</a>
        <ul>
          <li>
            <a href="#h4-deep-enough">'H4, deep enough?'</a>
            <ul>
              <li>
                <a href="#and-an-h5">And an h5 😬</a>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <a href="#lists">Lists</a>
  </li>
  <!-- More item here... -->
</ul>

The pyramid of doom

If you try to render the list without recursion, very soon you’ll be faced with the pyramid of doom:

const TableOfContents = ({ toc }) => (
  <ul>
    {(toc.items || []).map(item => (
      <li key={toc.url}>
        <a href={toc.url}>{toc?.title}</a>
        {(item.items || []).map(item => (
          <ul>
            <li>
              <a href={toc.url}>{toc?.title}</a>
              {/* ❌ The pyramid of doom ❌ */}            </li>
          </ul>
        )}
      </li>
    ))}
  </ul>
)

You could keep adding maps there until you’re deep enough and you’d probably be fine with it, but is that good code?

Recursion

It’s much nicer to have a function that calls itself as many times as is needed, React components can be recursive like any other functions:

const TocItem = ({ toc }) => {
  const subItem = (toc.items || []).map(item => (
    <ul key={item.url}>
      {/* ✅ Recursion ✅ */}
      <TocItem toc={item} type="child" />    </ul>
  ))

  return (
    <li key={toc.title}>
      <a href={toc.url}>{toc.title}</a>
      {subItem}
    </li>
  )
}

const TableOfContents = ({ toc }) => (
  <ul>
    {(toc.items || []).map((item, index) => (
      <TocItem key={index} toc={item} />    ))}
  </ul>
)

Conclusions

Some programmers don’t like recursion, and that’s fair, you don’t need to stuff it everywhere. Sometimes it just happens to be the right tool for the job.

Hope this was helpful, thanks for reading!

Comments would go here, but the commenting system isn’t ready yet, sorry. Tweet me @intterne if you want to make a correction etc.

  • © 2022 Antti Hiljá
  • About
  • Follow my new Twitter account → @intterne
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!