Processing of Lists and Keys
First, let's review how to traverse lists in JavaScript.
In the following code, we use the map() function to get an array of numbers and double their values. We assign the new array returned by map() to the variable doubled and record it:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(item => item * 2);
console.log(doubled); //=> [2, 4, 6, 8, 10]
The above code will be printed on the console [2, 4, 6, 8, 10].
In React, converting an array into a collection of elements is almost the same as the code above.
Rendering multiple components
You can create your own collection of elements and include them in JSX using curly brackets {}.
Next, we use the Javascript map() function to loop through an array of numbers. We return a < li > element for each item.
Finally, we assign the elements of the result array to listItems:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map(item => <li>{item}</li>);
We include the entire listItems array in the < UL > element and render it to DOM:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
)
This code will display numbers between 1 and 5.
Basic List components
Usually you put lists in components. We can reconstruct the previous example into components that accept digital arrays and output an unordered List of elements.
import React from 'react';
import ReactDOM from 'react-dom';
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item => <li>{item}</li>);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
When you run this code, you will receive a warning, Everychild in an array or iterator should have a unique "key" prop. Check the render method of "NumberList".
The prompt indicates that an attribute key should be provided for each item in the list. "Key" is a special string attribute that needs to be included when creating a list of elements. We will discuss why it is important in the next section.
Let's assign a key to the list item of numbers.map() and fix the problem that the prompt lacks a key.
import React from 'react';
import ReactDOM from 'react-dom';
function NumberList(props) {
const numbers = props.numbers;
- const listItems = numbers.map(item => <li>{item}</li>);
+ const listItems = numbers.map(item => <li key={item.toString()}>{item}</li>);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers}/>,
document.getElementById('root')
)
Use of Key
keys help determine which item s have been changed, added or deleted. The key attribute should be set on the elements in the array to give the elements a stable identity:
const numbers =[1, 2, 3, 4, 5];
const listItems = numbers.map(item =>
<li key={item.toString()}>
{item}
</li>
);
The best way is to use a string to select key, which is the unique identifier of a list item between its brothers. Typically, you use the ID in the data as the key:
const todoItems = todos.map(todo =>
<li key={todo.id}>
{todo.text}
</li>
);
When you do not have a stable ID for the rendered item, you can use item index as a keyword:
const todoItems = todos.map((todo, index) =>
// If todo does not have a stable id, you can use this method
<li key={index}>
{todo}
</li>
);
If the project needs to be reordered, we do not recommend using indexes for key s, because this will be slow.
Reasonable Extraction of Keys from Components
Keys only make sense in the context of loops.
For example, if you extract a ListItem component, you should save the key in the < ListItem array /> Elementally, not on the root < li > element of ListItem itself.
Example: Misuse of key
function ListItem(props) {
const value = props.value;
return (
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item =>
// That's wrong. key should be set here.
<ListItem value={item} />
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers}> />,
document.getElementById('root')
)
Example: The correct use of key
function ListItem(props) {
// That's right. There's no need to set key here.
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item =>
// That's right. Set the key here.
<ListItem key={item.toString()} value={item} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
)
Note: All elements in map() need the attribute key. Set the key wherever the loop is.
key must be unique in sibling components
The key used in an array should be unique among its sibling components. But they need not be global only. When we generate two different arrays, we can use the same key:
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map(post =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map(post =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'hello zhangyatao', content: 'you\'re so handsome!'},
{id: 2, title: 'hi jiangyanyun', content: 'you\'re so beautiful!'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
)
Keys are used as observation points for React, but they are not passed to your components. If you need to use the same value in a component, you explicitly pass it as props using different names:
const content = posts.map(post =>
<Post key={post.id} id={post.id} title={post.title} />
);
Using the example above, the Post component can read props.id, but not props.key.
Embedding map() in JSX
In the example above, we declared a separate listItems variable and included it in JSX:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item =>
<ListItem key={item.toString()} value={item} />
);
return (
<ul>{listItems}</ul>
);
}
JSX allows any expression to be embedded in braces, so we can inline map() results:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map(item =>
<ListItem key={item.toString()} value={item} />
)}
</ul>
);
}
This will make our code clearer, and this style can be used at will.
Like in JavaScript, it's up to you to decide whether it's worth extracting the readability of a variable.
Remember, if the map() body is nested too many layers, it's a good time to pull out a component.