Learn How to Use Map in React In and Out
When working with React apps, you will often find yourself using arrays in all sorts of situations, whether it be for working with state or rendering a list of items. When rendering a list of items, the map
array method is used.
In this tutorial, you will learn how to use the map
array method in React to loop over elements and create repeating code. We will also take a look at performance considerations and some common mistakes that you should avoid when working with map
. But first, why do we need to use map
in the first place?
Why Do We Need to Use Map?
The map
array method in JavaScript is used for transforming an array using a callback function. The map
loops through the array, and each item is transformed into whatever is returned from the array. For example, the following code doubles the values inside the array:
// This will return `[2, 4, 6, 8, 10]`
[1, 2, 3, 4, 5].map(i => i * 2)
Why do we need to usemap
in React? What is the problem with a regularfor
loop or aforEach
loop?
The reason we cannot use a regular for loop in JSX is that a for loop is a statement, but JSX always expects an expression to be returned. As we cannot break or return from a forEach
in JavaScript, a map
is used as a declarative way to loop over arrays in React.
How to Use Map in React
Now that we have this out of the way, let's see how to actually use map
in React. Most commonly, map
is used for displaying a list of elements in a component.
For the most part, this data often comes from an API. Imagine that we have a dynamic menu for which we receive the data in the following format:
{
"menu": [
{
"name": "Home",
"link": "/"
},
{
"name": "About us",
"link": "/about"
},
{
"name": "Contact",
"link": "/contact"
}
]
}
We can use a JSX expression to map through the array and display the elements using a map
in the following way:
import React from 'react'
const App = () => {
const menu = [
{ name: 'Home', link: '/' },
{ name: 'About Us', link: '/about' }
]
return (
<ul>
{menu.map((item, index) => (
<li key={index}>
<a href={item.link}>{item.name}</a>
</li>
))}
</ul>
)
}
export default App
Here, we used the Array.map
method inside a JSX expression to display each element with its name
and link
. We can return the required elements from the function. This is equivalent to the following, where we explicitly write out the return
keyword:
<ul>
{menu.map((item, index) => {
return (
<li key={index}>
<a href={item.link}>{item.name}</a>
</li>
)
})}
</ul>
Note that when using return
statements inside loops, we need to wrap them inside curly braces. When no curly braces are present, the return
is implied in an arrow function.
// Return is implicit
menu.map(item => <li>{item}</li>)
// Return is explicit
menu.map(item => {
return <li>{item}</li>
})
Generally speaking, an implicit return
is used most of the time. Using return explicitly can come in handy when we need to generate some type of data at each iteration in the loop and some minor extra logic is required. For example:
<ul>
{menu.map((item, index) => {
const label = `#${index + 1} `;
return (
<li key={index}>
<a href={item.link}>{label + item.name}</a>
</li>
)
})}
</ul>
Using the key prop
You may have noticed that we are using a unique key
prop on each li
in the above examples. The key
prop in React is used to identify each item in an array of items that are being rendered as a list. It helps React optimize the rendering process by allowing it to keep track of each item in the list.
The key
prop must be unique among siblings, and it should remain the same for the lifetime of the component. Commonly, a unique identifier such as an ID or index is used as the value. Therefore, avoid using Math.random
as the key for a component, as it doesn't provide unique values and duplicate keys can occur.
// β Don't use Math.random for a key
{posts.map(post => <Post details={post} key={Math.random()} />)}
Omitting the key
prop will result in the following warning: "Warning: Each child in a list should have a unique key
prop."
Outsourcing loops
Last, but not least, it's worth mentioning that we can also extract loops before the render. This can be done by defining a function before the return
keyword in the component:
import React from 'react'
const App = () => {
const menu = [
{ name: 'Home', link: '/' },
{ name: 'About Us', link: '/about' }
]
const MenuItems = () => menu.map((item, index) => (
<li key={index}>
<a href={item.link}>{item.name}</a>
</li>
))
return (
<ul>
<MenuItems />
</ul>
)
}
export default App
A function that returns JSX always needs to be capitalized. Otherwise, it will be treated as a regular function.
Performance Considerations
Working with map
in React is the recommended way to create loops. However, when not used optimally, it can cause performance issues. To help you avoid them, here are the three most common issues to look out for:
- Rendering long lists: One of the most common issues you can run into when using the
map
function in React is performance issues when rendering a large number of items. This can cause performance issues due to the time it takes to generate and update the virtual DOM. In this case, you may want to consider using pagination or infinite scrolling to improve performance:
const posts = [
{ link: '/map-in-react', title: 'How to work with maps in React' },
[0 ... 999]
]
// β Don't
{posts.map((post, index) => (
<li key={index}>
<a href={post.link}>{post.title}</a>
</li>
))}
// βοΈ Do use infinite scroll
<InfiniteScroll posts={posts} />
// βοΈ Do use pagination
const start = 0
const limit = 50
const page = posts.slice(start, limit)
{page.map((post, index) => (
<li key={index}>
<a href={post.link}>{post.title}</a>
</li>
))}
- Frequent updates: Frequent updates are another common cause of poorly performing
map
functions in React. Usually, this can be solved alone by addressing the first point and avoiding loading a large list. If you still experience problems, it will most likely be because you are re-rendering the entire list, even if only a few items change. In this case, memoize components usingReact.memo
:
// β Don't
const Post = ({ details }) => { ... }
// βοΈ Do memoize components
const Post = React.memo(({ details }) => { ... })
{posts.map(post => <Post details={post} />)}
- Performing expensive calculations: Avoid performing expensive computations during rendering on large lists, such as reordering the entire array or updating the state of each element. In this case, you want to perform these calculations before the rendering process. Another option is to batch similar calls and execute them once on a user action or to memoize callbacks:
// β Avoid
{gallery.map((image, index) => (
<li key={index}>
<img
src={image.src}
alt={image.alt}
onClick={calculationHeavyFn}
/>
</li>
))}
// βοΈ Do batch similar calls
<React.Fragment>
{gallery.map((image, index) => (
<li key={index}>
<img ... />
</li>
))}
<button onClick={calculationHeavyFn}>Call once</button>
</React.Fragment>
// βοΈ Do memoize callbacks
const memoizedFn = useCallback(() => calculationHeavyFn(value), [value]);
{gallery.map((image, index) => (
<li key={index}>
<img onClick={memoizedFn} />
</li>
))}
Common Mistakes
You also need to be aware of the most common mistakes people make when using map
in React. The three most common mistakes are:
- Not using the
key
prop: One of the most common mistakes new React developers make is omitting thekey
prop. Thekey
prop is required, and forgetting it can lead to unexpected behavior.
// β Don't forget the key prop
{posts.map(post => <Post details={post} />)}
// β Don't use Math.random for a key
{posts.map(post => <Post details={post} key={Math.random()} />)}
// βοΈ Do use a unique key
{posts.map((post, index) => <Post details={post} key={index} />)}
// β
Prefer a unique ID
{posts.map(post => <Post details={post} key={post.id} />)}
- Mutating the original array: Another common mistake people make when working with
map
in React is trying to mutate the original array when they want to update the state. In React, state is considered read-only. Always use the updater function to update the state:
// β Don't mutate the original array. This won't work.
<React.Fragment>
<ul>
{state.map(item => <li key={item}>{item}</li>)}
</ul>
<button onClick={() => state.push(state.length + 1)}>
Add new item
</button>
</React.Fragment>
// βοΈ Always use the updater function
<button onClick={() => setState([...state, state.length])}>Add new item</button>
- Not checking for
null
: When working with remote data, it is inevitable to run into cases where data will be missing. Not checking fornull
orundefined
can cause your application to crash when you receive data you don't expect. This can be resolved by using a logical AND or optional chaining:
// β Don't forget to check for optionality
{posts.map(post => <Post details={post} id={post.id} />)}
// βοΈ Do check for the presence of the data
{!!posts.length && posts.map(post => <Post details={post} key={post.id} />)}
// β
Prefer using optional chaining
{posts?.map(post => <Post details={post} id={post.id} />)}
Use double negation with length
to prevent rendering the length of the array.
Conclusion
By avoiding these mistakes, you can ensure that your React apps will be reliable and performant. Working with map
in React is straightforward once you are familiar with the rules and core concepts. In summary, here are the key takeaways when working with map
inside React:
- Always use
map
- Always use the
key
prop - Always check for falsy values
- Consider lazy loading when working with long lists
If you would like to learn more about React, check out our guided roadmap where you can learn more about where to start and which concepts you need to master to become proficient in React. Congratulations on reaching the end! Happy coding! π¨βπ»
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: