React從入門到精通系列之(8)Lists和Keys的處理

8、Lists和Keys的處理

首先,讓咱們回顧一下如何在JavaScript中遍歷lists。javascript

下面的代碼,咱們使用map()函數獲取一個數字數組,並將它們的值加倍。 咱們將map()返回的新數組賦給變量doubled並記錄下來:java

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中,將數組轉換爲元素集合幾乎和上面的代碼是同樣的。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:dom

ReactDOM.render(
    <ul>{listItems}</ul>,
    document.getElementById('root')
)

此代碼將會顯示1到5之間的數字。ide

基礎的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".post

提示指出應該爲列表的每一項提供一個屬性key「key」是建立元素列表時須要包含的特殊字符串屬性。 咱們將在下一節討論爲何它很重要。code

讓咱們給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()主體嵌套太多層,那麼它是抽出一個組件的好時機。

相關文章
相關標籤/搜索