react文档——列表和键
列表和键
首先,让我们先回顾一下在 JavaScript 中如何改变列表。
在下面给出的代码中,我们对numbers
数组调用map()
函数使它们的值变成双倍。我们把通过map()
返回的新数组赋值给变量doubled
并打印它。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
这段代码在控制台打印[2, 4, 6, 8, 10]
。
在 React 中,将数组转换成元素列表的方法几乎相同。
渲染多个组件
你可以构建元素的集合并使用大括号{}
把它们插入到 JSX 中。
下面,我们通过对numbers
数组调用map()
函数进行迭代。我们对每一项返回一个li
元素。最后,我们把元素数组的结果赋值给listItems
:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
我们把全部的listItems
数组包裹在一个ul
元素中并渲染到 DOM。
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
在CodePen上试试。
这段代码显示从numbers
迭代出来 1 到 5 之间的列表。
基础的列表组件
通常你会在一个组件内部渲染列表。
我们将之前的例子重构为一个组件,使其接收一个numbers
数组并输出一个无序列表的元素。
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。“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')
);
在CodePen上试试。
键
键帮助 React 识别哪个项发生了改变,被添加还是被移除。键应该被赋予数组内部的元素,以给元素一个固定的标识:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
选择一个 key 的最好的方式是使用一个在兄弟姐妹之间唯一标识一个列表项的字符串。很多时候你可能会从你的数据中使用 IDs 作为键:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
当你对渲染的项没有固定的 IDs 时,作为最后的手段,你可以使用项的索引作为一个 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
我们不建议使用索引作为键,如果列表项重新排序,那将是缓慢的。如果你感兴趣的话可以阅读深入讲解为什么键是必需的。
提取带键的组件
键只有在数组的上下文环境中有意义。
例如,你提取一个ListItem
组件,你应该在数组的<ListItem />
元素上使用 key,而不是在ListItem
本身的根元素<li>
上。
例子:错误的 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')
);
例子:改正后的 key 用法
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')
);
在CodePen上试试。
一个好的经验法则是:map()
调用内部的元素需要键。
在兄弟姐妹之间的键必须是唯一的
数组内使用的键在它们的兄弟姐妹之间必须是唯一的。然而它们不需要在全局唯一。当我们生成两个不同的数组时可以使用相同的键。
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')
);
在CodePen上试试。
键的作用是给 React 提示,但它们不会传递给你的组件。如果在你的组件中需要相同的值,使用不同的名字明确的作为 prop 传递它。
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
上面的例子中,Post
组件可以读取props.id
,但不能读取props.key
。
在 JSX 中插入 map()
在上述例子中,我们声明了一个独立的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 允许在大括号中插入任意表达式,所以我们可以内联map()
的结果:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
在CodePen上试试。
有时这会产生清晰的代码,但这种风格也会被滥用。就像在 JavaScript 中,它由你决定是否值得为了可读性提取一个变量。记住,如果map()
体有太多的嵌套,这是提取组件的好时机。
下一步
表单