首先 ,让我们回顾下JavaScript中的列表。
下面的例子里,我们使用函数map()来遍历一个列表的元素,并得到元素值得两倍。map()函数返回这个两倍于旧元素新的数组并打印log:
看看这两个组件:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
代码在控制台输出[2 , 4 , 6 , 8 , 10]。
在React中,将数组转换成元素列表几乎是相同的。
渲染多个组件
使用花括号{}来构造一个元素集合或把JSX写在里面。
下面 ,我们通过map()函数遍历一个number数组。让每个元素都返回自己的’
- ‘。最终,我们把(一个
- 的集合)结果赋给变量 listItems。
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
我们用
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
这段代码呈现了一个从1—5的列表。
基本列表元素
通常你需要渲染组件里面的列表。
我们重构先前的例子,使它接受一个数字数组参数的并且输出一个无序列表。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
代码运行后,你发现有一个错误,说你需要给你的列表的元素一个Key(bundle.js:1289 Warning: Each child in an array or iterator should have a unique “key” prop.)。“key”是一个特殊的字符串属性,当你构造列表时,你需要提供这个属性。下一节中我们将讨论为什么这点如此重要。
让我们在numbers.map()中分配key给每个列表项来修复这个丢失了key的问题。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Keys
Key帮助React识别哪个元素发生了变更,是增加了?或者被移除了。数组中的元素应该被赋予一个具有稳定性的Key:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
挑出Key最好的方法是使用一个独立标识于其他兄弟元素的字符串。最常见的就是使用数据里面的ID作为Key:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
万不得已时(当你没有一个稳定的ID来渲染元素时),你可能使用元素索引作为key。
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
要是列表中的次序会改变,我们不推荐使用这种索引Key。这会造成性能问题还会导致关于组建状态的诸多问题。查看Robin Pokorny的论文 in-depth explanation on the negative impacts of using an index as a key.。如果你选择不是分配一个明确的key,那么儿啊春天就会默认使用索引作为key。
有兴趣了解更多,看看进一步解释为什么Key是必要的。
根据Key提取组件
Key只在数组相关的上下文中生效。
举例说明,要是你提取组件:ListItem,你需要把key保持在身处数组中的元素中,而不是定义时的
- 元素中。
例子:错误用法
function ListItem(props) {
const value = props.value;
return (
// Wrong! There is no need to specify the key here:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! The key should have been specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
例子:正确用法
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
一个好的经验是:元素在map()调用中使用key。
Key在兄弟元素中必须独一无二
Key的使用在一个数组中不能重复,可是,并不需要在全局中唯一。当我们构造了两个不一样的数组是,我们可以使用相同的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 World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
Key的作用就像是给React暗示,但是它不能向下层组件传递。你要是需要相同的值,那你就另外一个prop名字,显式的把这个值传递。
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
上述例子中,组件Post能读懂props.id,但是不能读懂props.key。
在map()中嵌入JSX
在上面的例子中,我们生命了一个ListItems变量并在里面写了JSX:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX允许在任何花括号{}中嵌入表达式,所以我们也能把JSX内联在在map()的返回中:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
有时候这种写法清晰,有时候这种写法也让人迷惑。就像JavaScript,为了可读性而把它提取为变量,这完全决定于你。有一点要记住,要是map()中嵌套的太复杂,那么这将是一个提取成组件的好时机。