八、Lists和Keys的處理
首先,讓我們回顧一下如何在JavaScript中遍歷lists。
下面的程式碼,我們使用map()
函式獲取一個數字
陣列,並將它們的值加倍。 我們將map()
返回的新陣列賦給變數doubled
並記錄下來:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(item => item * 2);
console.log(doubled); //=> [2, 4, 6, 8, 10]
上面的程式碼會在控制檯列印[2, 4, 6, 8, 10]
。
在React中,將陣列轉換為元素集合
幾乎和上面的程式碼是一樣的。
渲染多個元件
您可以自己一個建立元素集合,並使用花括號{}
將它們包含在JSX中。
下面,我們使用Javascript map()
函式迴圈一個數字陣列。 我們為每個item
返回一個<li>
元素。 最後,我們將結果陣列的元素賦給listItems
:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map(item => <li>{item}</li>);
我們將整個listItems
陣列包含在<ul>
元素中,並將其渲染到DOM:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById(`root`)
)
此程式碼將會顯示1到5之間的數字。
基礎的List元件
通常你會將List放在元件中。
我們可以將前面的例子重構為接受數字陣列的元件,並輸出一個無序的元素列表。
import React from `react`;
import ReactDOM from `react-dom`;
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item => <li>{item}</li>);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById(`root`)
);
當您執行此程式碼時,將會收到一條警告,
Each child in an array or iterator should have a unique "key" prop. Check the render method of "NumberList".
提示指出應該為列表的每一項提供一個屬性key
。 “key”
是建立元素列表時需要包含的特殊字串屬性。 我們將在下一節討論為什麼它很重要。
讓我們給numbers.map()
的列表項分配一個key
,並修復那個提示缺少key
的問題。
import React from `react`;
import ReactDOM from `react-dom`;
function NumberList(props) {
const numbers = props.numbers;
- const listItems = numbers.map(item => <li>{item}</li>);
+ const listItems = numbers.map(item => <li key={item.toString()}>{item}</li>);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers}/>,
document.getElementById(`root`)
)
Key的用法
keys
幫助確定哪些item
已更改,已新增或已刪除。 應該給陣列中的元素設定上key
屬性,以便給元素一個穩定的身份:
const numbers =[1, 2, 3, 4, 5];
const listItems = numbers.map(item =>
<li key={item.toString()}>
{item}
</li>
);
最好的方法是使用一個字串
來選擇key
,它是其兄弟之間一個列表項的唯一標識
。 通常,您會使用資料中的ID作為key:
const todoItems = todos.map(todo =>
<li key={todo.id}>
{todo.text}
</li>
);
當您對已渲染的item沒有穩定的ID時,您可以將專案index用作關鍵字:
const todoItems = todos.map((todo, index) =>
// 如果todo沒有一個穩定的id,可以使用這種方法
<li key={index}>
{todo}
</li>
);
如果專案需要實現重新排序,我們不建議為key使用索引,因為這將很慢。
合理提取元件中的Keys
Keys僅在迴圈時的上下文中有意義。
例如,如果您提取了一個ListItem
元件,則應該將該key儲存在陣列中的<ListItem />
元素上,而不是ListItem
本身的根<li>
元素上。
示例:key的錯誤用法
function ListItem(props) {
const value = props.value;
return (
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(item =>
// 這是錯誤的,這裡應該設定上key
<ListItem value={item} />
);
return (
<ul>{listItems}</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(item =>
// 這才是正確的,在這裡設定key
<ListItem key={item.toString()} value={item} />
);
return (
<ul>
{listItems}
</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 zhangyatao`, content: `you`re so handsome!`},
{id: 2, title: `hi jiangyanyun`, content: `you`re so beautiful!`}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById(`root`)
)
key用來作為React的觀察點,但它們不會傳遞給您的元件。 如果你需要在元件中使用相同的值,則使用不同的名稱顯式地將它作為props傳遞:
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(item =>
<ListItem key={item.toString()} value={item} />
);
return (
<ul>{listItems}</ul>
);
}
JSX允許在大括號中嵌入任何表示式,所以我們可以內聯map()
結果:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map(item =>
<ListItem key={item.toString()} value={item} />
)}
</ul>
);
}
這會讓我們的程式碼更清晰,這種風格可以被隨意使用。
就像在JavaScript中,它是由你決定是否值得提取一個變數的可讀性。
請記住,如果map()
主體巢狀太多層,那麼它是抽出一個元件的好時機。