Lists and Keys 首先,让我们回顾一下,在js中如何变换列表。 下面给出的代码,我们使用 'map()' 函数作用一个 'numbers' 数组,并且使数组中的元素值*2.我们分配由 'map()' 返回的新数组给变量 'doubled',并且在console中输出:
const numbers = [1, 2, 3, 4, 5];
const doubled = number.map((numer) => number * 2);
console.log(doubled);
这段代码在console中输出:[2, 4, 6, 8, 10]
在React中,变换数组为元素列表,同js中差不多是一样的。
渲染多个组件
你可以构建元素集合,并且通过使用 '{}',在JSX中来引入它们。
下面,我们通过js的 'map()' 函数来循环 'numbers' 数组。对数组的每个条目,返回一个 '<li>' 元素。最终,我们分配元素结果数组给 'listItems':
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
我们在一个 '<ul>' 元素内包含整个 'listItems' 数组,并且渲染为DOM:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
这段代码展示了一个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' 是一个特殊的字符串属性,当创建元素列表时需要引入。在下个小结,我们将讨论为什么它很重要。
让我们分配一个 'key' 给我们的列表条目,在 'numbers.map()' 内部,并且修复这个丢失的key的问题。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}> // 这里新增了key属性
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
keys
keys帮助React识别列表中哪个条目修改过、新增的或者被移除。数组内的元素应该给出keys属性,以赋予元素一个稳定的标识:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
选择key的最佳方法是使用一个字符串,来唯一标识列表项的每一个元素。大多数情况下,你会使用数据的IDs作为keys:
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>
);
如果列表条目可能被重排,我们不建议使用条目索引作为key,因为这样会很慢(React计算条目哪些被修过、是新增的,还是移除了哪些条目,比较缓慢)。如果你感兴趣,可以阅读 'in-depth explanation about why keys are necessary' 这节。
使用keys来提取组件
keys只有在数组的上下文中才有意义。
例如:如果你提取一个 'ListItem' 组件,你应该保证 <ListItem> 元素上的key在这个数组中,而不是在根 <li> 元素在 'ListItem' 自身上。
不正确的key的使用:
function ListItem(props) {
const value = props.vaue;
return (
// 错误!这里不需要指定key
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 错误!key应该在这里被指定
<ListItem value={number} />
);
return (
<ul>
{listItem}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOm.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
正确的key的使用:
function ListItem(props) {
// 正确!这里不需要制定key
return <li>{props.value}</li>
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正确!key应该在数组中被指定
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItem}
</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')
);
keys作为一个提示服务于React,但它们并不传递给你的组件。如果在你的组件中需要同样的值,将 key 作为一个 prop(使用不同的名字) 来明确地传递它:
const content = post.map((post) =>
<Post
key={post.id} // key只是React用于检测列表条目的变化(不会供我们内部props使用)
id={post.id} // 我们再定义一个id,内部就可以通过 props.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>
{listItem}
</ul>
);
}
JSX允许在 '{}' 内嵌入任意表达式,因此我们可以内联 'map()' 的结果:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
)}
</ul>
);
}
有时候,这样更清晰明了,但是这种方式有可能被滥用。就像在js中,它取决于你,来决定为了可读性,是否值得提取一个变量。牢记:如果 'map()' 体也是嵌套的,这可能是一个提取组件的好时机。