React Rendering Lists

Chapter 12 14 mins

Learning outcomes:

  1. When could we need to render lists
  2. Rendering lists using for
  3. Rendering lists using map()
  4. Lists and the key prop

Introduction

In the previous chapter, we learnt about conditionally rendering stuff in React, that is, rendering given stuff based on the outcome of a given condition.

In particular, we saw four ways to perform conditional rendering: using if, using the logical AND (&&) operator, using the logical OR (||) operator, and finally using the conditional operator (?:).

Conditional rendering is more than just common in React userland code. And the same is the case for rendering lists.

In this chapter, we shall discover the common functional practice that React utilizes to render arrays of items, i.e. using the map() array method. We'll start off by discussing why the traditional, imperative for loop would be verbose in React code and then present a better alternative using map().

As stated earlier, the usage of map() in userland code in React is just as popular as, or perhaps even more popular than conditional rendering. Likewise, comprehending it is more than important for every single React developer.

We'll start off with a very basic question: When could we possibly need to render lists in React?

When could we need to render lists?

Suppose we have the following array containing the names of a couple of programming languages:

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

We want to take this list and produce an <ol> element out of it, where each <li> child contains a particular item from the list.

That is, we want to get the following output in the HTML:

<ol>
   <li>JavaScript</li>
   <li>PHP</li>
   <li>Ruby</li>
   <li>C#</li>
</ol>

This output needs to be returned from our App component.

To learn more about the App component, head over to React Components: The defacto App component.

Now, obviously, as we have an array of items to work with here, a fairly straightforward thought is that we'd get back an array of <li> elements as well, representing those items (in some way).

This array of <li> elements is ultimately provided while instantiating <ol> in React, and thus represents a case of rendering a list in React.

Recall the following from the React Basics chapter:

A React node can be either of the following: React elements, strings, arrays, Booleans, numbers, undefined, and null.

Notice the array type in there. It simply means that an array/list can also be used as a unitary node in a createElement() call.

In this discussion, we're referring to arrays and lists interchangeably. That is, they mean the same thing.

And when we're working with arrays, as with languages in the code above, providing an array to createElement(), or equivalently providing an array as a child of a JSX element, isn't uncommon.

Now that it's clear when could we need to render a list in React, let's see exactly how to convert a mere list of items, such as languages, into a list of React elements.

Rendering lists using for

The first choice that may come to mind is to use a for loop, iterating over a given list, converting each item into a corresponding React element, and then using this list inside a container element.

Let's do this for the languages array shown above and then see the problem with such an approach.

Here's our App component to begin with, with access to the languages array from the outside (this isn't common in real-world but alright for this demonstrative purpose):

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

function App() {
   return (
      <ol></ol>
   );
}

We just need to write some additional code here to generate a list of <li> elements using languages and then pass that to <ol>.

Let's create that list now using a for loop, specifically using a for...of loop:

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

function App() {
   var liElements = [];
   for (var language of languages) {
      liElements.push(
         <li>{language}</li>
      );
   }

   return (
      <ol>{liElements}</ol>
   );
}

The liElements array is used to hold the list of <li> elements generated using the for...of loop, and then added inside the <ol> element.

Let's test the output before anything else:

Live Example

Yep, it's working.

Now, it's time to talk through an issue with this approach.

Recall from the previous chapter on conditional rendering that components in React don't intrinsically blend as well with statements (like if) as they do with expressions (like the logical AND (&&) operator).

The reason is because JSX elements are themselves mere expressions and so if we need to get a true templating feel while writing JSX, we need to work with expressions as well.

Talking about the code above, the for loop is a perfectly valid approach for generating a list of elements — there's nothing wrong in it, per se. But because it's a statement and not an expression in JavaScript, we can't inline it within JSX code.

That is, we can't do the following:

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

function App() {
   return (
      <ol>
         {for (var language of languages) {
            <li>{language}</li>
         }}
      </ol>
   );
}

This is just absolutely wrong from top to bottom!

And that's a problem.

When we're working with JSX and when we're inside an element, it's desirable to be able to render a list, after processing that list, in situ. In other words, it's great to have some way of iterating over the list and then returning an appropriate list of elements back right within the JSX element.

The for loop doesn't allow us to do so. Then what can we use for this?

Well, recall the map() method of arrays — it's precisely what we need here.

Rendering lists using map()

The super useful map() method available on arrays in JavaScript helps us map an array to another array based on a mapping function.

For example, let's say we have an array of some integers as follows:

var ints = [1, 2, 3, 4, 5];

We can easily map this array of integers to an array of their squares using map() as follows:

var ints = [1, 2, 3, 4, 5];
var squares = ints.map(int => int ** 2);

console.log(squares);
[1, 4, 9, 16, 25]

The mapping function is called on each element in the underlying array and the return value of that call is placed in the new, mapped array.

For example, the mapping function called on element 4 returns 16 and that gets placed in the mapped array in the fourth position ([1, 4, 9, 16, 25]).

In JSX code, map() proves to be really handy for two reasons:

  1. It is an expression, which means that it could be used in JSX.
  2. It returns back another array, which means that its return value could be directly used inside a JSX element.

Let's rewrite the code above, where we created a list of <li> elements using a languages array, with the help of map().

The idea is pretty basic: map the languages array to an array of <li> elements containing those items. That is, each item of languages gets mapped to an <li> element containing that item.

Here's the code:

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

function App() {
   return (
      <ol>
{languages.map(language => (
<li>{language}</li>
))} </ol> ); }

Notice how the entire logic of rendering a list of <li> elements is nicely contained inside the <ol> element, exactly where it should ideally be. This is the beauty of map().

Anyways, let's see the output produced:

Live Example

Voila! It works perfectly.

Lists and the key prop

In both the code snippets above where we successfully rendered a list of elements, we get a strange warning in the console that reads as follows:

Warning: Each child in a list should have a unique "key" prop.

What did we miss in our code?

Well, we forgot to add a key prop to each <li> element in the list.

And now what exactly is key? Let's figure that out.

The key prop is used by React internally to distinguish between a set of children of a given element. All the children of an element, where key makes sense, are supposed to be assigned a unique value to their key props.

By design, React requires the existence of a key prop on every element of a rendered list.

But why?

In short, keys help React remember elements between multiple renders in order to stay away from recreating DOM nodes for those elements (which would otherwise be redundant).

It's difficult to explain keys right now because it requires a go-through on a couple of different concepts of React. We'd be able to understand the intuition behind keys only after we learn about reconciliation — a core idea in React. Then we shall talk about keys in detail in the React Keys chapter later on in this course.

For now, let's just see how to solve the warning issued in the case of missing key props.

The solution is pretty simple: we ought to set a key prop on each <li> element of the list whose value must be unique in that list.

In a list of elements, keys are supposed to be unique and that's precisely how React is able to distinguish between different elements across multiple renders, i.e. using these unique keys.

One common technique to obtain a unique value for each element is to simply use the index of the corresponding item in the list. That is, the first element (with the index 0) gets the key 0, the second one gets the key 1, and so on.

This is demonstrated as follows:

const languages = ['JavaScript', 'PHP', 'Ruby', 'C#'];

function App() {
   return (
      <ol>
         {languages.map((language, i) => (
            <li key={i}>{language}</li>
         ))}
      </ol>
   );
}

The index is obtained with the help of the second argument of the callback given to map().

As you can check in the console, this workaround does calm down the key warning to our benefit.

However, it's important to realize the pitfalls of using list item indexes as keys while we're doing so, and that we shall cover in the React Keys chapter.