React main concepts(八)
建议配合官网文档 Lists and Keys 进行阅读。
Lists and Keys
下面给出了一个代码,使用map()
函数将一个数组的每个值扩大至两倍:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(number => number * 2);
console.log(doubled);
我们将一个数组的每一个值都变成了其值的两倍,同样,我们也可以将每一个值都变成一个其对应的 React 元素。
Rendering Multiple Components
下面,我们通过map()
函数将一个数组中的每一项变成一个<li>
元素,即从包含数字的数组变成包含 React 元素的数组:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map(
number => <li>{number}</li>
);
我们将<li>
数组中的元素放入<ul>
元素,并渲染到 DOM 中,React 会自动展开数组:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
)
这个代码将展示了1-5的<li>
标签。
Basic List Component
一般来说,我们会在一个组件里渲染数组,因此我们将上面的代码重构为一个组件,该组件接受一个数组作为参数,返回一个<ul>
的元素。
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'));
当你运行这行代码时,会出现一个 warning:Warning: Each child in an array or iterator should have a unique "key" prop.
,警告表示,在数组中的每个孩子都需要一个key
参数,key
是数组中每个数组项的一个特殊的字符串属性,让我们详细了解一下key
的重要性。
Keys
key
可以帮助 React 识别哪一个数组项被添加、删除、或者修改了,也就是说,通过key
,React 可以在数组中唯一确定一个数组项:
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'));
对于一个数组项来说,最好的key
是使用数组项的id
属性,在实际应用中,数据往往是存放在数据库中的,因此使用数据库中的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>
);
注意,当数组中的数组项顺序可能发生改变时,我们强烈不
建议使用数组下标作为key
,这有可能对 React 的性能造成影响,或者引起组件的状态问题,详细的情况可以参考in-depth explanation on the negative impacts of using an index as a key.如果你不指定key
的值,则 React 会默认使用数组下标作为key
,所以还是尽可能找到每个数组项的固有属性作为key
的值。
如果你想深入了解为什么key
是必要的,可以参考in-depth explanation about why keys are necessary
Extracting Components with Keys
key
只作用在数组的上下文中,这句话可能比较难理解,我们不考虑如下实例:
如果你提取了一个ListItem
组件,则你只需要保证数组项ListItem
拥有自己的key
,而不必考虑给ListItem
内部的元素设置key
:
错误的例子:
function ListItem(props) {
const value = props.value;
return (
// 这是错误的,因为这里没有数组的概念,li 不是数组项 不需要在这里指定 key
<li key={value.toString()}>
{value}
</li>
)
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(number =>
// 这是错误的,这里 listItems 是数组,因此需要在遍历的同时给数组项设置`key`
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
)
}
正确的写法应该如下所示:
function ListItem(props) {
const value = props.value;
// 这里不是数组项,因此不需要设置 key
return (
<li>{value}</li>
)
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(
// 这里是数组项,因此需要设置 Key
number => <ListItems value={number} key={number.toString()}/>
);
return (
<ul>
{ListItems}
</ul>
)
}
一个很好的经验法则是:
map()
函数中生成的元素往往需要设定key
。
Keys Must Only Be Unique Among Siblings
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.content}</h3>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
)
}
const posts = [
{id: 1, title: "title1", content: "content1"},
{id: 2, title: "title2", content: "content2"},
];
ReactDOM.render(<Blog posts={posts}/>, document.getElementById('root'));
key
虽然作为参数传递给了组件,但是由于key
是专门用与 React 进行数组项识别的,因此在 props 里无法访问key
,如果你想获取key
的值,则需要明确的使用一个新参数进行传递。