React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.
In this article, we’ll look at how to render lists in a React component.
Lists and Keys
We can transform lists into HTML by calling the array’s map
method.
For example, if we want to display an array of numbers as a list, we can write:
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{[1, 2, 3, 4, 5].map(num => (
<span>{num}</span>
))}
</div>
);
}
}
In the code above, we called map
on the [1, 2, 3, 4, 5]
array. In the map
method’s callback, we returned a span
with the number inside and we do the same for each element.
Then we get:
12345
displayed on the screen.
We can assign it to a variable and then pass it into the ReactDOM.render
method as follows:
import React from "react";
import ReactDOM from "react-dom";
const nums = [1, 2, 3, 4, 5].map(num => <span>{num}</span>);
const rootElement = document.getElementById("root");
ReactDOM.render(nums, rootElement);
We’ll get the same items displayed.
Also, we can use the same code inside the render
method:
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
const nums = [1, 2, 3, 4, 5].map(num => <span>{num}</span>);
return <div>{nums}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Keys
When we render lists, we should provide a value for key
prop for each rendered element so that React can identify which items have changed, added, or removed.
It gives elements a stable identity. We should pick a key by using a string that uniquely identifies a list item among its siblings.
A key should be a string value.
For example, if we want to render a list of to-do items, we should pick the id
field as the key
‘s value as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ id: 1, text: "eat" },
{ id: 2, text: "drink" },
{ id: 3, text: "sleep" }
]
};
}
render() {
return (
<div>
{this.state.todos.map(todo => (
<p key={todo.id.toString()}>{todo.text}</p>
))}
</div>
);
}
}
In the code above, we have the key={todo.id.toString()}
prop to set the key
to the todo
‘s id
converted to a string.
If we have no stable identity for our items, we can use the index for the entry as a last resort:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [{ text: "eat" }, { text: "drink" }, { text: "sleep" }]
};
}
render() {
return (
<div>
{this.state.todos.map((todo, index) => (
<p key={index.toString()}>{todo.text}</p>
))}
</div>
);
}
}
index
is always available and it’s unique for each array element, so it can be used as a value for the key
prop.
Extracting Components with Keys
If we render components, we should put the key
prop in the component rather than the element that’s being rendered.
For example, the following is incorrectly using the key
prop:
function TodoItem({ todo }) {
return <p key={todo.id.toString()}>{todo.text}</p>;
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ id: 1, text: "eat" },
{ id: 2, text: "drink" },
{ id: 3, text: "sleep" }
]
};
}
render() {
return (
<div>
{this.state.todos.map(todo => (
<TodoItem todo={todo} />
))}
</div>
);
}
}
In the TodoItem
component, we have:
<p key={todo.id.toString()}>{todo.text}</p>;
with the key
prop. We don’t want this there because we don’t need to identify a unique li
since it’s isolated from the outside already. Instead, we want to identify a unique TodoItem
.
Instead, we should write:
function TodoItem({ todo }) {
return <p>{todo.text}</p>;
}class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ id: 1, text: "eat" },
{ id: 2, text: "drink" },
{ id: 3, text: "sleep" }
]
};
}
render() {
return (
<div>
{this.state.todos.map(todo => (
<TodoItem todo={todo} key={todo.id.toString()} />
))}
</div>
);
}
}
We should add keys to items return with map
‘s callback.
Keys Only Need to Be Unique Among Siblings
Keys only need to be unique among sibling elements.
For example, we can write:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [
{ id: 1, text: "eat" },
{ id: 2, text: "drink" },
{ id: 3, text: "sleep" }
],
comments: [
{ id: 1, text: "eat" },
{ id: 2, text: "drink" },
{ id: 3, text: "sleep" }
]
};
}
render() {
return (
<div>
<div>
{this.state.posts.map(post => (
<p key={post.id.toString()}>{post.text}</p>
))}
</div>
<div>
{this.state.comments.map(comment => (
<p key={comment.id.toString()}>{comment.text}</p>
))}
</div>
</div>
);
}
Since posts
and comments
aren’t rendered in the same div
, the key
values can overlap.
The key
prop doesn’t get passed to components. If we need the same value in a component we have to use a different name.
Embedding map() in JSX
We can embed the expression that calls map
straight between the curly braces.
For example, we can write:
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{[1, 2, 3, 4, 5].map(num => (
<span key={num.toString()}>{num}</span>
))}
</div>
);
}
}
Conclusion
We can use JavaScript array’s map
method to map values to React elements or components.
To help React identify elements unique, we should pass in a unique string to key
prop for each entry rendered.
It should be passed to anything that’s returned by map
‘s callback since they’re the items that have to be uniquely identified by React.
Also, keys only have to be unique among direct siblings. Other places can have the same key values.