One of the most common tasks for React developers is to render a list of data (e.g. users) onto a web page. And thanks to the the Array.map
method this can be done in a simple and efficient way. In this article, you’ll learn how JSX helps to make components in React as close to HTML as possible while still allowing you to write some JavaScript.
Let’s start by looking at how to use JSX to render dynamic data to the page using React. If you want to insert a dynamic value into the HTML you can place an expression inside curly braces like so:
<h1>Hello {name}!</h1>
This is all fairly straightforward, but eventually you'll run into a situation where you want to insert a list of data into a component. Lists of data are fairly common in websites and apps, for example:
- lists of tweets on Twitter
- playlists of videos on YouTube
- lists of photos on Instagram for example
And when you need to display insert a list of values in React, the Array.map
function will become your new best friend!
Why not use for loops instead of map? 🤔
The reason for this is that you can't use for
loops in JSX. This is because anything inside the curly braces in a component has to be an expression: which means you can't use imperative statements like for
or if
. But don't worry, JavaScript arrays have some powerful methods that will help you transform the data into a value that can be inserted directly into components.
How does map work?
Array.map
works in a similar way as a mathematical function.
Take a look at the diagram below (from Maths Is Fun). It's a mathematical function mapping one set of numbers on the left, to another set of numbers on the right. The mapping is applying a rule to each number. Can you figure out what this rule is?
Hopefully you figured out that the rule is to square the number on the left (i.e. multiply it by itself) to get the number on the right. In mathematical notation, this would be written as:
n → n²
(note how similar this looks to an arrow function in JavaScript by the way).
An array of values in JavaScript can be thought of as the sets of numbers used in the example above. The Array.map
function allows us to apply the same function to every value in an array and produce a new array containing the results. Each value in the original array maps to a corresponding value in the new array.
For example, consider the following array of purple shapes. If we apply the mapping function 'make green' to it then every shape in the original array gets mapped to a green-colored shape in the new array.
In a similar way, applying the 'make love' function to the following array of shapes will return an array where every element in the original array has been mapped to a love heart of the same color:
The Array.map
method works in the same way as a mathematical function. It iterates over the array that calls the method and returns a new array, with each value from the calling array mapped to a corresponding new value, based on a mapping function.
For example, the following mapping function would double every number:
const double = n => 2 * n
The mapping function is provided as an argument to the Array.map
method. Functions that are provided as an argument are often referred to as callbacks. The fact that Array.map
accepts a function as an argument means that it is a higher order function, which is any function that accepts another function as an argument or returns a function.
Another important point is that the Array.map
method doesn't mutate the original array that calls it. It returns a completely new array of new values and leaves the original array unchanged.
This ability to map each item in an array to a new value without changing the underlying structure of the array means that JavaScript arrays are examples of a functor.
Array.map examples
Let's take a look at some examples of using the Array.map
method in JavaScript.
Mapping numbers
First of all, let's create an array of numbers that we can apply these mapping functions to:
const numbers = [1, 2, 3, 4, 5]
Now let's define some mapping functions that can be used to map one value to another:
const increment = n => n + 1
const double = n => 2 * n
const square = n => n * n
Here we have three mapping functions:
increment
returns the next number after the argumentdouble
returns the argument multiplied by 2square
returns the argument multiplied by itself
Let's have a go at using them:
numbers.map(increment)
// result [2,3,4,5,6]
As you can see, when the increment
mapping function is provided as an argument, the array that is returned has the same number of elements as the numbers
array, but each corresponding value has been incremented by one.
We can also confirm that the numbers
array hasn't been changed by the operation:
numbers
// result [1, 2, 3, 4, 5]
If we provide the double
mapping function as an argument, it will return an array where each value is twice as big as the corresponding value in the numbers
array:
numbers.map(double)
// result [2, 4, 6, 8, 10]
Since the Array.map
method returns a new array, it's often helpful to assign the returned value to a variable. For example, we can provide the square
mapping function as an argument to the numbers
array, which will return a new array that correspond to the square numbers, so it makes sense to assign the return value to the variable squareNumbers
:
const squareNumbers = numbers.map(square)
// result [1, 4, 9, 16, 25]
If we don't have a named mapping function that performs the operation we need, we can provide an anonymous function as an argument. The following example will subtract each value in the numbers
array from 10:
numbers.map(n => 10 - n)
// result [9, 8, 7, 6, 5]
We can also chain mappings together and apply two functions to each value in the numbers
array:
numbers.map(double).map(increment)
// result [3, 5, 7, 9, 11]
This will double all the numbers and add one to them all. However, this is an example of just because you can do something, doesn’t mean you should. If you want to double the numbers then add one, it would be much more efficient to write a new mapping function that does this instead of applying the Array.map
method twice. This is because every time you use the Array.map
method, you loop over the array, so applying it twice will result in two passes of the array. This isn’t much of a problem when the array isn’t very long, but in reality you may be dealing with huge sets of data and should try and limit the number of times you loop over the array.
Mapping strings
We can also use the Array.map
method to transform an array of strings. To demonstrate this, let's create an array called words
:
const words = ["hello","world"]
And let's create a mapping function that accepts a string as an argument and returns that string written in reverse:
const reverse = string => [...string].reverse().join()
Providing the reverse
mapping function as an argument to words.map
will return a new array with each word written backwards:
words.map(reverse)
// result ["olleH", "dlroW"]
We can also use built-in string methods by providing an anonymous function as a callback that calls the method we want on each string in the array. In the following example, we call the toUpperCase
string method on each word in the array:
words.map(word => word.toUpperCase())
// result ["HELLO", "WORLD"]
This returns a new array with each word written in capital letters.
A common pattern when mapping over strings is to insert them into HTML tags. For example, we could wrap heading tags around the words using the following mapping function:
const makeHeading = text => <h1>${text}</h1>
If we provide the makeHeading
function as an argument to the mapping over the words
array, it will return an array of HTML:
This HTML can then be inserted into a web page.
Mapping objects
We can also use Array.map
to extract information from a list of objects. For example, we could have a list of objects that describe different types of fruit, something like this:
const fruits = [
{ name: "Apple"
price: 25
emoji: "🍏"
},
{ name: "Banana"
price: 40
emoji: "🍌"
},
{ name: "Melon"
price: 75
emoji: "🍉"
},
]
The first thing we can do is extract all the values of a specific property from each object. For example, if we only wanted to see the emoji
property, we could use the following code:
fruits.map(fruit => fruit.emoji)
// result ["🍏","🍌","🍉"]
This maps each object to a single value that corresponds to the emoji
property of each object.
We can also map over an array of objects to create a string of HTML based on the object's values. The following code will create a list item based on each object in the fruits
array:
const fruitList = fruit.map(
fruit => `<li>${fruit.emoji} ${fruit.name}, ${fruit.price}c each</li>`)
This will return the following array of HTML:
["<li>🍏 Apple, 25c each</li>","<li>🍌 Banana, 40 each</li>","<li>🍉 Melon, 75c each</li>"]
This example shows how you can take a load of data stored in an array of objects and use map to create an array of HTML. This is the foundation of using Array.map
to create JSX code that can be used in React components.
Using map in React
The Array.map
function comes into its own when we need to insert a list of data from an array into a JSX component. Let's take a look at a simple example using this array of super heroes:
const heroes = ["Superman", "Batman", "Wonder Woman"]
We can create a component that uses Array.map
to iterate over each super hero in the heroes
array and create a level-1 heading containing the name of each super hero:
const Headings = () => {
const headings = heroes.map((hero, index)=>
<h1 key={index}>{hero}</h1>)
return <header>{headings}</header>
}
The return value of Array.map
is stored in the variable headings
. This can now be inserted as an expression into the return value of the the Headings
component. Each element in the heroes
array will then be rendered as a heading.
You can see this example on CodePen.
This example shows how easy it is to use Array.map
to display a list of data using a single line of code.
Using keys
An important point to remember when using Array.map
to create a list of items in React is that you must provide a key
prop. This is used by React in the background to keep track of the order of the items in the list. It becomes particularly important if the items in the list are likely to change order or if items are added or removed from the list.
React uses a virtual DOM to keep track of any changes that are made and keys are important in making sure that these changes are as rendered in the most efficient way possible.
An easy way to create a key
prop is to use the index of the array:
const Headings = () => {
const headings = heroes.map((hero,index) => <h1 key={index}>{hero}</h1>)
return <header>{headings}</header>
}
This is not best practice however, as the index of each element will change if the underlying data changes and won't help the virtual DOM keep track of any changes.
Ideally each element needs to use a unique value as its key
prop. Thankfully most data provided by a database or API usually comes with unique IDs that we can use as the key
prop.
Lists of objects
Let's finish off by looking at a more realistic example where you have an array of objects that have been pulled from a database or external API. For example, the following list of coding books:
const books = [
{ id: "B09123N2QN",
title: "Learn To Code With JavaScript",
author: "Darren Jones",
price: 2260,
pages: 425,
cover: "https://m.media-amazon.com/images/I/412KSS+3fjL._SX260_.jpg",
keywords: ["coding", "javascript", "beginner"]
},
{ id: "B088P9Q6BB",
title: "JavaScript: The Definitive Guide",
author: "David Flanagan",
price: 2399,
pages: 708,
cover: "https://m.media-amazon.com/images/I/51wijnc-Y8L._SX260_.jpg",
keywords: ["rhino", "javascript", "mastery"]
},
{ id: "B07C96Q217",
title: "Eloquent JavaScript",
author: "Marijn Haverbeke",
price: 1994,
pages: 474,
cover: "https://m.media-amazon.com/images/I/51-5ZXYtcML.jpg",
keywords: ["eloquent", "javascript", "modern"]
},
]
If we wanted to display a list of books, showing the title and cover, we could create a Book component that returned the JSX we required:
const Book = ({id,title,cover}) =>
<li key={id}>
<h2>{title}</h2>
<img alt={`cover of ${title}`} src={cover} />
</li>
This takes the book object and uses destructuring in the parameter list to extract the id
, title
and cover
properties. These are then used to return a list item that displays the title as a level-1 heading and the cover of the book as an image.
Now we can create a BookList
component that uses Array.map
to create a list of books by using the Book
function as the mapping function that is provided as an argument:
const BookList = () =>
<div>
<h1>Books</h1>
<ul>
{books.map(Book)}
</ul>
</div>
The BookList
component creates a container <div>
and heading and then provides the <ul>
elements to contain the list of books. We then use Array.map
to loop over the list of books and insert a Book
component in place of each value in the books
array.
You can see this example on CodePen.
Limiting the list
This is fine in this case as the books
array only contains 3 books, but in reality an API might return hundreds of book objects, so how can you limit the number of books that are displayed using the map
method? It's actually quite straightforward, you can just use the slice
method to cut the array down to size, for example, if you only want to display the first 2 books, you can update the BookList
component to the following:
const BookList = ({n}) =>
<div>
<h1>Books</h1>
<ul>
{books.slice(0,n).map(Book)}
</ul>
</div>
It's much better to slice the array before applying the map function as avoids the whole of the books
array being iterated over.
We can make the BookList
component a bit more general by providing the number of books to display in the list as a prop, to the component:
const BookList = ({n}) =>
<div>
<h1>Books</h1>
<ul>
{books.slice(0,n).map(Book)}
</ul>
</div>
Now we can choose the value of n
that will determine the number of books displayed in the list. This is called when we render the BookList
component, like so:
ReactDOM.render(<BookList n={2} />, document.getElementById("root"))
Displaying data in a table
The last example used map
to create an unordered list of items, which is an obvious way to present data from an array, but we can also use it to iterate over data and present it in other ways as well, for example we could create a table to display all the information about our books
array:
const BookRow = ({id,title,author,price,pages}) =>
<tr key={id}>
<td>{title}</td>
<td>{author}</td>
<td>{pages}</td>
<td>£{price/100}</td>
</tr>
Then we can create the actual table to hold all these rows:
const BookTable = () =>
<div>
<h1>Books</h1>
<table>
<thead>
<th>Title</th>
<th>Author</th>
<th>Number of Pages</th>
<th>Price</th>
</thead>
<tbody>
{books.map(BookRow)}
</tbody>
</table>
</div>
This sets up the table and table headings and then after the heading row, we use Array.map
to map over each book in the books
array and pass it to the BookRow
component. This has the effect of creating a new row for every element in the books
array.
You can see this example on CodePen.
Creating a nested list
Sometimes an array will contain objects that also contain arrays as properties. These might need to be mapped over as well in order to display them using JSX. In this case we can create a nested map statement.
For example, our books
array contains a property called keywords
which is an array of key words about each book. What if we wanted to display the title of each book with its keywords listed underneath like tags?
First of all we could create Tag
component:
const Tag = word =>
<div className="tag">{word}</div>
Then we create a Book
component, similar to the one we used earlier:
const Book = ({id,title,keywords}) =>
<li key={id}>
<h2>{title}</h2>
<div>{keywords.map(Tag)}</div>
</li>
This time we use Array.map
again map over the keywords array, which will insert a new Tag
component inside a <div>
element for each keyword in the array.
Last of all we need a BookList
component that will map over the books
array and create a list of Book
components (this component is exactly the same as the one we used earlier):
const BookList = ({n}) =>
<div>
<h1>Books</h1>
<ul>
{books.slice(0,n).map(Book)}
</ul>
</div>
You can see an example on CodePen.
We can see here that the Array.map
in the BookList
component iterates over the books
array and then the Array.map
in the Book
component iterates over every element in the keywords property of every element in the books
array.
The verdict
The Array.map method is a powerful higher-order function that lets you transform all the values in an array using a mapping function. This is especially useful for inserting a list of data into a React app, since you can't use for
loops. It lets you replace elements in an array with a component based on the data in the array. When dealing with data that will change, it is important that you provide a unique key
prop for each component in the list. Once you’ve got the hang of using the Array.mapmethod to display lists of data into React, you’ll wonder how you ever managed without it!