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.
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
, andnull
.
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.
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:
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:
- It is an expression, which means that it could be used in JSX.
- 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:
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:
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.
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.