My ‘Well, DUH!!!’ moment with React arrays

Update 6/27/17: After it was pointed out to me that React frowns on using index numbers for keys, I rewrote parts of this post.

While I haven’t been coding for very long, one thing that has already happened to me several times is that big ‘A-HA’ moment. That moment when something you’ve been staring at for way too long finally clicks into place and just starts to make sense.

This feeling can be great! Most of the time. You truly feel like you’ve learned something or made it over an obstacle that once looked like an impossible climb. Other times, that feeling can also be accompanied by a ‘Well, DUH!!! Why didn’t I see that an hour ago?!?’

The latest one of those times just happened to me while working through an online React course. The instructor was writing some code that would iterate over an array with a .map() method and output each item in the array to the screen. Fairly simple stuff. Except he added one oh-so-simple step that gave me that ‘Well, DUH!!!’ feeling and solved an issue that I had with every array I’ve used in React up to this point.

An array example

Here is a simple example that illustrates the issue. Below is some code that takes a list of TV shows in an array, iterates over that array, and displays each to the screen.

import React, { Component } from 'react';
class ShowList extends Component {
constructor(props) {
super(props);
this.state = {
tvShows : ['Game of Thrones', 'Last Week Tonight', 'Silicon Valley', 'Veep', 'The Leftovers']
};
}
render() {
return (
<ul>
{this.state.tvShows.map((show, i) => {
return <ShowItem show={show} />
})}
</ul>
);
}
}
const ShowItem = (props) => {
return (
<li>{props.show}</li>
)
}
export default ShowList;

The results, as you might expect, look like this:

I spend a lot of time with HBO Now. Can you tell?

Again, very simple and straightforward. Until you open up the console and see this:

All children in an array need a ‘key’.

What is this? In React, when you iterate over an array like this, each child in that array need to have a ‘key’ property that is unique to that child.

In other projects I’ve worked on when doing this, I was pulling data from an API and some part of the data had a unique key or ID value that could be plugged in here. But what about a simple list like this? There is no additional data to use.

The suggested way

In an ideal world, the IDs you need for your keys would come from your data. Whether that data is something you provide or it comes from a third-party API. Here I am adding an ‘id’ to each of the tvShows and that will become the key.

this.state = {
tvShows : [
{title: 'Game of Thrones', id: 0},
{title: 'Last Week Tonight', id: 1},
{title: 'Silicon Valley', id: 2},
{title: 'Veep', id: 3},
{title: 'The Leftovers', id: 4}
]
};

And then change the .map() to this:

render() {
return (
<ul>
{this.state.tvShows.map((show) => {
return <ShowItem show={show.title} key={show.id} />
})}
</ul>
);
}

The output is exactly the same and a ‘key’ prop is now included.

Great! End of post. Good job everybody!

But ‘Wait.’ you say? ‘What if we want to add another show to our list of shows?’, you say? No problem, go ahead, have fun! Add another show with another unique ID. Done!

‘What if we want to add 100 more shows?’, you say?

Ridiculous!! There is only enough room in the world for ‘…maybe five computers.’ and lists will never be over 6 items long!

Okay, all kidding aside, obviously this method isn’t scalable. There is another solution for this situation and it is courtesy of the .map() method itself.

.map() to the rescue

It turns out the the .map() method can take more than one argument. (Well, DUH!!!). According to the documentation, it can actually take several. One of them being an ‘index’.

That’s right! .map() can return the index value of an array for you, and an index sounds like a solution for generating that ‘key’ prop that React is looking for.

Here is an example of giving the second argument ‘i’ to .map() which is asking it to keep track of the index of each element in the array it is passed and then logging out both the element and its index.

The result is both the element and it’s index.

This approach can now be applied to the first example of TV Shows.

this.state = {
tvShows : ['Game of Thrones', 'Last Week Tonight', 'Silicon
Valley', 'Veep', 'The Leftovers']
};
render() {
return (
<ul>
{this.state.tvShows.map((show, i) => {
return <ShowItem show={show} key={i} />
})}
</ul>
);
}

Now when the above list gets rendered, each element has a key assigned to it and there are no more errors in the console.

We can also confirm this by looking at the React devtools in Chrome.

I hope this helped shed some light on working with array in React and using the features of the .map() method. Please take a second to recommend this post if you thought it was helpful or ask and questions or make comments below. Thanks for reading!