Creating Reusable React Components with map()
Tags: react, functional, javascript, a11y
Published: 2020-01-22 14:51:49

## 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](https://en.wikipedia.org/wiki/Functional_programming).

## What is `map()`?

The method `map()` is a higher-order Javascript[^1] 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.

```javascript
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) for more information!

[^1]: Map can be called on arrays and other data structures in other languages as well, and it works in a similar way!

## 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`.  

```javascript
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](https://reactjs.org/docs/jsx-in-depth.html).

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](https://reactjs.org/docs/lists-and-keys.html#keys).

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](https://en.wikipedia.org/wiki/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 styling[^2] 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>
  );
};
```

[^2]: [See the docs](https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type#Accessibility_concerns) for info on how to make unordered lists with no list style type more accessible.

And that's it! 

⚡️[Try it out on Codesandbox!](https://codesandbox.io/s/reusable-card-component-n2ifm)

{% codesandbox reusable-card-component-n2ifm %}