首先,让我们回顾一下你是如何用javascript来转换一个列表。在下面的代码中,我们用map函数来使得一个数字数组中的数加倍并且将它打印到控制台:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
上面这段代码将在控制台打印出[2,4,6,8,10],在react中将数组转化为列表的方法和上面的方法类似。
渲染多个组件
你可以创建一个element的集合并把它们包裹在打括号中,下面的代码用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')
);
上面的代码在屏幕上输出了标为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。当你创建列表是,必须为每一个项目指定特殊的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')
);
关键字
关键字可以帮助react确定哪些列表项被改变,添加或者移除。关键字应该被指定给列表中的每一项以便它们有一个确定的ID:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
你通常会从你的数据中获取ID来为每一项指定关键字:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
如果你没有一个确定的ID赋给列表项目的话,你可以使用列表的索引值来当作它的关键字:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
如果这些列表项会被重新排序的话,我们并不建议你使用索引值来当作其关键字,因为这样会使得渲染变的缓慢。
基于关键字抽象组件
关键字仅仅在在包裹它的数组中起作用。比如,如果你抽象出一个名为ListItem的组件,你应该将关键字添加在ListItem的声明上,而不是在LIstItem组件中绑定关键字:
- 错误的用例
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函数中绑定关键字 **
一个列表中的列表项的关键字是唯一的
在一个列表中,所有列表项的关键字必须是唯一的,当然,这仅仅在这个列表中如此,全局范围内的列表项的关键字不必全都不一样。我们可以将两个相同的关键字用在不同的数组中:
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')
);
关键字为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允许我们将表达式插入其中,所以我们可以这样写:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
有时候,上面的表达式是十分简洁的,但是也有些许困惑,就像在javascript里面一样。现在,是使用这种简洁的方式呢,还是像前面的代码一样将其抽象成一个变量呢,完全取决于你。还有,若是map函数中的嵌套的话,将嵌套的东东抽象成一个组件是很棒的实践方式。