Introduction
When creating components in React, we sometimes notice that certain components are repeated. Using Javascript's map()
method, we can make these repetitive components more reusable. In this post, we'll explore a method of creating reusable card components using this gem of functional programming.
What is map()
?
The method map()
is a higher-order Javascript1 method that can be called on an array, and returns another array. The results of the returned array depend on what we tell the method to do to each element of the array. We tell map
what we want to do to each array element by passing a callback.
Below, I've used the simplest version of this callback: where its only argument is the individual array element, and the work we want done to each of those elements is returned in the callback function. After the map
method iterates through each array element and does the work on that element, it returns an array with the modified elements. The elements in the returned array are in the same order that they were sequenced in the original array.
const mappedArray = [1, 2, 3, 4].map(element => element + 1); // [2, 3, 4, 5]
The callback can accept other arguments, like the index of the array elements being iterated over. Check the docs for more information!
The source array
First, we need an array to call the map method on. Here, I'm choosing to make things a bit more interesting by using an array of Javascript objects, each representing a superhero or supervillain. Each object has a property of id
, name
, alterEgo
, and alignment
.
const characters = [
{
id: 1,
name: "Wonder Woman",
alterEgo: "Diana Prince",
alignment: "hero"
},
{
id: 2,
name: "Poison Ivy",
alterEgo: "Pamela Lillian Isley",
alignment: "villain"
},
{
id: 3,
name: "Black Canary",
alterEgo: "Dinah Drake",
alignment: "hero"
},
{
id: 4,
name: "Catwoman",
alterEgo: "Selina Kyle",
alignment: "villain"
}
];
The component
Next, we'll need the component. We're making an unordered list where a card component represents a list item. This is more semantic and accessible than creating a list with just div
s. Below, I've provided the skeleton for our component, that has placeholders for where the name, alter-ego, and the alignment values will go.
const CardList = () => {
return (
<ul>
<li>
<div className="card-container">
<p>
<strong>Name</strong>
</p>
<p>AlterEgo</p>
<p>Alignment</p>
</div>
</li>
</ul>
);
}
Putting it together
With map()
, we can return an array of functions. Functions are "first-class citizens" in Javascript, so they can be passed around and returned like any string or number (which is why we can pass callbacks in the first place!). React components themselves are functions: written in JSX, the React.createElement()
method is being called under the hood.
With our source array and list element above, we can return an array of list elements that are populated with the values of the character object properties we choose to access.
const CardList = () => {
return (
<ul>
{characters.map(character => {
return (
<li>
<div className="card-container">
<p>
<strong>{character.name}</strong>
</p>
<p>{character.alterEgo}</p>
<p>{character.alignment}</p>
</div>
</li>
);
})}
</ul>
);
};
Here, we call map()
on our array of objects, and we are supplying a callback that tells map()
what we want done to each object. Each object in the array is the element that is passed as an argument to that callback. As we are iterating, we are returning a list item element. In the paragraph tags, we are accessing the value of each object's property.
🌟 Bonus! Refactoring
We can take this component to the next level by making the list item into its own component.
As a best practice, we should also add a unique key
to each list item. This helps React re-render components more efficiently, since it now only has to observe changes in a particular, uniquely identified repeated element in order to re-render, instead of re-rendering the entire component array when any one component element changes. Again, see the docs.
For the refactor, we'll want to use the unique id
property of each element in the array for the list item key
. If our source data was coming from some kind of database, using that record's primary key would work here, too.
The only thing that our list item component needs is the character object, so we'll pass that in as a prop. I've added some inline styling2 to make the list items look more like cards.
const CardListItem = props => {
return (
<li>
<div
className="card-container"
style={{
width: "50%",
border: "solid 3px #d3d3d3",
margin: "10px auto"
}}
>
<p>
<strong>{props.character.name}</strong>
</p>
<p>{props.character.alterEgo}</p>
<p>{props.character.alignment}</p>
</div>
</li>
);
};
And this is how CardListItem
can be used. Notice that we are using the character object's id
property as the component's key
:
const CardList = () => {
return (
<ul style={{ listStyleType: "none" }}>
{characters.map(character => {
return <CardListItem character={character} key={character.id} />;
})}
</ul>
);
};
And that's it!
-
Map can be called on arrays and other data structures in other languages as well, and it works in a similar way! ↩
-
See the docs for info on how to make unordered lists with no list style type more accessible. ↩
Top comments (4)
Oh my goodness! Thank you! My learning plan just introduced JSX and React and my head has been swimming... I had no idea how much I needed your sentence "React components are themselves functions..." That really helped me arrange the react puzzle piece into the larger picture.
Thank you! It's very helped me with my card component for my very first website on React ⚛️!
Powerful woman
This is a pretty good explanation!! It helps me a lot, I'm just a beginner in React, articles like this one are gold!!