React.js 小書 Lesson13 - 渲染列表數據
轉載請注明出處,保留原文鏈接以及作者信息
在線閱讀:http://huziketang.com/books/react
列表數據在前端非常常見,我們經常要處理這種類型的數據,例如文章列表、評論列表、用戶列表…一個前端工程師幾乎每天都需要跟列表數據打交道。
React.js 當然也允許我們處理列表數據,但在使用 React.js 處理列表數據的時候,需要掌握一些規則。我們這一節會專門討論這方面的知識。
渲染存放 JSX 元素的數組
假設現在我們有這么一個用戶列表數據,存放在一個數組當中:
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
如果現在要把這個數組里面的數據渲染頁面上要怎么做?開始之前要補充一個知識。之前說過 JSX 的表達式插入 {}
里面可以放任何數據,如果我們往 {}
里面放一個存放 JSX 元素的數組會怎么樣?
...
class Index extends Component {
render () {
return (
<div>
{[
<span>React.js </span>,
<span>is </span>,
<span>good</span>
]}
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
我們往 JSX 里面塞了一個數組,這個數組里面放了一些 JSX 元素(其實就是 JavaScript 對象)。到瀏覽器中,你在頁面上會看到:
[圖片上傳失敗...(image-d59196-1510226499425)]
審查一下元素,看看會發現什么:
[圖片上傳失敗...(image-918e44-1510226499425)]
React.js 把插入表達式數組里面的每一個 JSX 元素一個個羅列下來,渲染到頁面上。所以這里有個關鍵點:如果你往 {}
放一個數組,React.js 會幫你把數組里面一個個元素羅列并且渲染出來。
使用 map 渲染列表數據
知道這一點以后你就可以知道怎么用循環把元素渲染到頁面上:循環上面用戶數組里面的每一個用戶,為每個用戶數據構建一個 JSX,然后把 JSX 放到一個新的數組里面,再把新的數組插入 render
方法的 JSX 里面。看看代碼怎么寫:
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
class Index extends Component {
render () {
const usersElements = [] // 保存每個用戶渲染以后 JSX 的數組
for (let user of users) {
usersElements.push( // 循環每個用戶,構建 JSX,push 到數組中
<div>
<div>姓名:{user.username}</div>
<div>年齡:{user.age}</div>
<div>性別:{user.gender}</div>
<hr />
</div>
)
}
return (
<div>{usersElements}</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
這里用了一個新的數組 usersElements
,然后循環 users
數組,為每個 user
構建一個 JSX 結構,然后 push 到 usersElements
中。然后直接用表達式插入,把這個 userElements
插到 return 的 JSX 當中。因為 React.js 會自動化幫我們把數組當中的 JSX 羅列渲染出來,所以可以看到頁面上顯示:
[圖片上傳失敗...(image-7ae938-1510226499425)]
但我們一般不會手動寫循環來構建列表的 JSX 結構,可以直接用 ES6 自帶的 map
(不了解 map
函數的同學可以先了解相關的知識再來回顧這里),代碼可以簡化成:
class Index extends Component {
render () {
return (
<div>
{users.map((user) => {
return (
<div>
<div>姓名:{user.username}</div>
<div>年齡:{user.age}</div>
<div>性別:{user.gender}</div>
<hr />
</div>
)
})}
</div>
)
}
}
這樣的模式在 JavaScript 中非常常見,一般來說,在 React.js 處理列表就是用 map
來處理、渲染的。現在進一步把渲染單獨一個用戶的結構抽離出來作為一個組件,繼續優化代碼:
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
class User extends Component {
render () {
const { user } = this.props
return (
<div>
<div>姓名:{user.username}</div>
<div>年齡:{user.age}</div>
<div>性別:{user.gender}</div>
<hr />
</div>
)
}
}
class Index extends Component {
render () {
return (
<div>
{users.map((user) => <User user={user} />)}
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
這里把負責展示用戶數據的 JSX 結構抽離成一個組件 User
,并且通過 props
把 user
數據作為組件的配置參數傳進去;這樣改寫 Index
就非常清晰了,看一眼就知道負責渲染 users
列表,而用的組件是 User
。
key! key! key!
現在代碼運作正常,好像沒什么問題。打開控制臺看看:
[圖片上傳失敗...(image-8a89e0-1510226499425)]
React.js 報錯了。如果需要詳細解釋這里報錯的原因,估計要單獨寫半本書。但可以簡單解釋一下。
React.js 的是非常高效的,它高效依賴于所謂的 Virtual-DOM 策略。簡單來說,能復用的話 React.js 就會盡量復用,沒有必要的話絕對不碰 DOM。對于列表元素來說也是這樣,但是處理列表元素的復用性會有一個問題:元素可能會在一個列表中改變位置。例如:
<div>a</div>
<div>b</div>
<div>c</div>
假設頁面上有這么3個列表元素,現在改變一下位置:
<div>a</div>
<div>c</div>
<div>b</div>
c
和 b
的位置互換了。但其實 React.js 只需要交換一下 DOM 位置就行了,但是它并不知道其實我們只是改變了元素的位置,所以它會重新渲染后面兩個元素(再執行 Virtual-DOM 策略),這樣會大大增加 DOM 操作。但如果給每個元素加上唯一的標識,React.js 就可以知道這兩個元素只是交換了位置:
<div key='a'>a</div>
<div key='b'>b</div>
<div key='c'>c</div>
這樣 React.js 就簡單的通過 key
來判斷出來,這兩個列表元素只是交換了位置,可以盡量復用元素內部的結構。
這里沒聽懂沒有關系,后面有機會會繼續講解這部分內容。現在只需要記住一個簡單的規則:對于用表達式套數組羅列到頁面上的元素,都要為每個元素加上 key
屬性,這個 key
必須是每個元素唯一的標識。一般來說,key
的值可以直接后臺數據返回的 id
,因為后臺的 id
都是唯一的。
在上面的例子當中,每個 user
沒有 id
可以用,可以直接用循環計數器 i
作為 key
:
...
class Index extends Component {
render () {
return (
<div>
{users.map((user, i) => <User key={i} user={user} />)}
</div>
)
}
}
...
再看看,控制臺已經沒有錯誤信息了。但這是不好的做法,這只是掩耳盜鈴(具體原因大家可以自己思考一下)。記住一點:在實際項目當中,如果你的數據順序可能發生變化,標準做法是最好是后臺數據返回的 id
作為列表元素的 key
。
下一節中我們將介紹《React.js 小書 Lesson14 - 實戰分析:評論功能(一)》。