React Rendering Lists Tutorial: Complete Guide to Map Method and Keys
Learn how to efficiently render lists of data in React using array methods and keys for optimal performance.
In many situations you’ll want to display multiple items that share the same structure and styling based on a collection of predefined data that might come from various sources including external APIs
, offline browser storages such as IndexedDB
or LocalStorage
and so on.
Even though the data might come from various sources it always ends up into a Javascript
array
.
So in this article we will learn how to render a list of components or just raw JSX
codes given an array
of data.
And then we will understand how React
can track those rendered components via something called keys
.
So if that sounds interesting let’s dive in.
Let’s say that you needed to render a list of items that have the same styling and structure but only differ in their content.
Instead of writing this whole code and repeating ourselves.
<ol>
<li>Item 1</li>
<li>Item 2</li>
...
<li>Item N</li>
</ol>
We can just define an array
of strings that will host all of our data. And then use the array
built-in map
method to transform each element in the array
into a different shape.
This is done by passing a callback function
that defines this transformation.
The callback function will be written as follows: we take the function’s argument and then we return this JSX
code that represents the targeted markup of each array element.
In our case we want to convert each item in the array from a string to JSX code which consists of the <li>
tags wrapping the string.
function App(){
const items = ["item 1","item2","item 3"]
const JSXItems = items.map(item=>(<li>{item}</li>))
// [<li>Item 1</li>,<>Item 2</li>,<>Item 3</li>]
return (
/*...code*/
)
}
Keep in mind, that the map method keeps the original array intact, and returns a new array that we can store in a newly created variable.
Okay, now what? We’ve got an array of JSX elements (JSXItems
). But how can we render that?
React
can only render two things: JSX
code or an array
of JSX
codes.
So we can safely render the content of the new array directly in the JSX through the curly bracket syntax.
The result will look like this.
function App(){
const items = ["item 1","item2","item 3"]
const JSXItems = items.map(item=>(<li>{item}</li>)) // array of JSX elements.
// [<li>Item 1</li>,<>Item 2</li>,<>Item 3</li>]
return (
<ol>
{JSXItems}
</ol>
)
}
Now let’s move to a more complex example.
Let’s say that we’ve got an array
of objects, each representing a todo and possessing the following attributes:
- An
id
that uniquely identifies each object. - A
todo name
,description
and aboolean
variable representing whether a given todo is completed or not.
const todos = [
{
id: 1,
name: "todo 1",
description: "todo 1 description",
completed: false,
},
{
id: 2,
name: "todo 2",
description: "todo 2 description",
completed: true,
},
{
id: 3,
name: "todo 3",
description: "todo 3 description",
completed: false,
},
];
As we’ve done previously we can use the map
array method
to transform this array of objects into an array of JSX
elements, representing each the shared Markup
between the todos with the corresponding array item custom data.
const todosJSX = todos.map(item=>(
<div>
<h2>{item.name}</h2>
<p>{item.description}</p>
<p>{item.completed ? ✅ : ❌}</p>
</div>
))
Now let’s just render the array in the App
component’s JSX
.
function App(){
// ...code for todos
const todosJSX = todos.map(item=>(
<div>
<h2>{item.name}</h2>
<p>{item.description}</p>
<p>{item.completed ? ✅ : ❌}</p>
</div>))
return (
<div>
{todosJSX}
</div>
)
}
So that’s how easy it is to render an array
of objects especially after we’ve gone through the previous simplified example. It’s that simple just map it to JSX
elements and you’re good to go 😄.
Now as the JSX returned by the map
’s callback function is getting quite long and messy let’s substitute it or move it into its own react component
First let’s Create a TodoItem component, receiving the necessary props and returning the previous JSX code.
function TodoItem({name,description,completed}){
return (
<div>
<h2>{name}</h2>
<p>{description}</p>
<p>{completed ? ✅ : ❌}</p>
</div>
)
}
Then let’s call it in the map
’s callback function, pass the props by spreading the todo object instead of passing each attribute separately.
function App(){
// ...code for todos
const todosJSX = todos.map(todo=>(<TodoItem {...todo}/>))
return (
<div>
{todosJSX}
</div>
)
}
As you can see bellow spreading the todo object makes the component more readable and maintainable.
const todosJSX = todos.map(todo=>(<TodoItem title={todo.title} description={todo.description} completed={todo.completed} />))
// VS
const todosJSX = todos.map(todo=>(<TodoItem {...todo}/>)) // ✅ Better and more concise
Now our code is much cleaner and easier to read.
Open loop
There is one more important and missing topic that we haven’t yet dealt with. Which is keys
.
When rendering a list of items without passing the key
prop into each. The following warning gets displayed in your browser console.
Warning: Each child in a list should have a unique “key” prop.
When using the map
method to render an array
of items, we should always pass a string
or a number
key
prop that uniquely identifies each item among other items in the array.
The keys help React
to understand which array item
each component corresponds to. Or you can think of it as a way to link your array data to the rendered components or JSX
items.
That may seem not important when the array
is not changing.
But if the array
gets mutated or changed by either sorting it, deleting or inserting an item and so on.
React
can have a hard time to track items because by default it’s using the item’s array index which is obviously not stable when deleting or inserting items or sorting the array.
The keys should also be stable, or in other terms they should not change every time the component gets rendered in order to optimize updating the DOM tree therefore your application’s performance when displayed on the user browser.
The easiest way to make sure that the id
is stable is to include it in the data itself as we did previously when defining todo list item object.
Right Inside the map’s callback function, let’s assign the todo.id
attribute to the component’s key prop.
const todosJSX = todos.map(todo=>(<TodoItem key={todo.id} {...todo}/>))
The same thing applies if JSX
code was returned just pass the key
prop to the upper wrapping element as shown bellow.
const todosJSX = todos.map(item=>(
<div key={todo.id}>
<h2>{item.name}</h2>
<p>{item.description}</p>
<p>{item.completed ? ✅ : ❌}</p>
</div>))
An interesting case that we should absolutely highlight here is when returning multiple JSX
items we’re required to use the fragment syntax because JSX
does not allow returning multiple children as you might know if you’ve read the previous JSX article.
If you have tried to set the prop key
, to the shorthand version of the React
fragment syntax, your editor will highlight it as a syntax error. Because that’s not allowed.
function Test({id}){
return (
<key = {id}> {/* Not allowed ❌*/}
<h1>Title</h1>
<div>Random description text</div>
</>
)
}
Instead you’re required to use the longer version which consists of wrapping your returned children with the React.Fragment
tag.
Then you can pass the key prop as you usually do with any JSX tag or component.
import React from "react"
function Test({id}){
return (
<React.Fragment key = {id}> {/* allowed ✅*/}
<h1>Title</h1>
<div>Random description text</div>
</React.Fragment>
)
}
So that’s how you simply render lists in React
. In the next article we’re going to unveil how we can you seamlesly respond events such as click or keyboard typing without even touching the browser’s DOM API.
Thank you for your attentive reading, and happy coding 🧑💻.