列表和鍵
首先,讓我們先回顧一下在 JavaScript 中如何改變列表。
在下面給出的代碼中,我們對numbers
數(shù)組調(diào)用map()
函數(shù)使它們的值變成雙倍。我們把通過map()
返回的新數(shù)組賦值給變量doubled
并打印它。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
這段代碼在控制臺打印[2, 4, 6, 8, 10]
。
在 React 中,將數(shù)組轉(zhuǎn)換成元素列表的方法幾乎相同。
渲染多個(gè)組件
你可以構(gòu)建元素的集合并使用大括號{}
把它們插入到 JSX 中。
下面,我們通過對numbers
數(shù)組調(diào)用map()
函數(shù)進(jìn)行迭代。我們對每一項(xiàng)返回一個(gè)li
元素。最后,我們把元素?cái)?shù)組的結(jié)果賦值給listItems
:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
我們把全部的listItems
數(shù)組包裹在一個(gè)ul
元素中并渲染到 DOM。
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
在CodePen上試試。
這段代碼顯示從numbers
迭代出來 1 到 5 之間的列表。
基礎(chǔ)的列表組件
通常你會在一個(gè)組件內(nèi)部渲染列表。
我們將之前的例子重構(gòu)為一個(gè)組件,使其接收一個(gè)numbers
數(shù)組并輸出一個(gè)無序列表的元素。
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')
);
當(dāng)你運(yùn)行這段代碼時(shí),你會得到一個(gè)警告:應(yīng)該為列表項(xiàng)提供一個(gè) key?!発ey”是一個(gè)特殊的字符串屬性,當(dāng)你創(chuàng)建一個(gè)元素列表時(shí),你需要包含它。我們會在接下來的部分討論問什么它這么重要。
讓我們給numbers.map()
內(nèi)部的列表項(xiàng)分配一個(gè)key
來修復(fù)缺少 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 識別哪個(gè)項(xiàng)發(fā)生了改變,被添加還是被移除。鍵應(yīng)該被賦予數(shù)組內(nèi)部的元素,以給元素一個(gè)固定的標(biāo)識:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
選擇一個(gè) key 的最好的方式是使用一個(gè)在兄弟姐妹之間唯一標(biāo)識一個(gè)列表項(xiàng)的字符串。很多時(shí)候你可能會從你的數(shù)據(jù)中使用 IDs 作為鍵:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
當(dāng)你對渲染的項(xiàng)沒有固定的 IDs 時(shí),作為最后的手段,你可以使用項(xiàng)的索引作為一個(gè) key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
我們不建議使用索引作為鍵,如果列表項(xiàng)重新排序,那將是緩慢的。如果你感興趣的話可以閱讀深入講解為什么鍵是必需的。
提取帶鍵的組件
鍵只有在數(shù)組的上下文環(huán)境中有意義。
例如,你提取一個(gè)ListItem
組件,你應(yīng)該在數(shù)組的<ListItem />
元素上使用 key,而不是在ListItem
本身的根元素<li>
上。
例子:錯(cuò)誤的 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上試試。
一個(gè)好的經(jīng)驗(yàn)法則是:map()
調(diào)用內(nèi)部的元素需要鍵。
在兄弟姐妹之間的鍵必須是唯一的
數(shù)組內(nèi)使用的鍵在它們的兄弟姐妹之間必須是唯一的。然而它們不需要在全局唯一。當(dāng)我們生成兩個(gè)不同的數(shù)組時(shí)可以使用相同的鍵。
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()
在上述例子中,我們聲明了一個(gè)獨(dú)立的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 允許在大括號中插入任意表達(dá)式,所以我們可以內(nèi)聯(lián)map()
的結(jié)果:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
在CodePen上試試。
有時(shí)這會產(chǎn)生清晰的代碼,但這種風(fēng)格也會被濫用。就像在 JavaScript 中,它由你決定是否值得為了可讀性提取一個(gè)變量。記住,如果map()
體有太多的嵌套,這是提取組件的好時(shí)機(jī)。
下一步
表單