過去在使用 Vue 的專案中,會看到利用 v-for 顯示待辦事項清單:
<!-- TodoList.vue -->
<TodoItem
v-for="todo in todos"
:key="todo.id"
:todo="todo"
/>
v-for="todo in todos" 會逐一取得 todos 裡的資料,並為每一筆資料建立一個 TodoItem Component。
啊再看到另一邊 React 專案,看到的不是 v-for,而是:
// TodoList.tsx
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
這裡的 map() 不是 React 提供的特殊語法,而是 JavaScript Array 本身就有的方法。
React 沒有另外設計一個清單指令,而是直接使用 JavaScript 的 map(),將陣列中的每一筆資料轉換成 JSX。
map() 會逐一讀取陣列裡的每個項目,並根據回傳的內容建立一個新陣列。
例如:
const todos = ['學習 JSX', '認識 Props', '理解 map']
const listItems = todos.map((todo) => (
<li key={todo}>{todo}</li>
))
map() 會執行三次,每次的 todo 分別代表陣列中的一筆資料,最後產生三個 <li>:
<li>學習 JSX</li>
<li>認識 Props</li>
<li>理解 map</li>
React 可以顯示包含多個 JSX Element 的陣列,因此可以將結果放進 <ul>:
return <ul>{listItems}</ul>
也可以直接將 map() 寫進 JSX:
return (
<ul>
{todos.map((todo) => (
<li key={todo}>{todo}</li>
))}
</ul>
)
所以在 React 中,map() 的工作可以先理解成:
將陣列中的每一筆資料轉換成一個 JSX Element。
map()回到目前 Todo List 專案中的程式碼,這裡的 todos 放的不是單純的字串,而是像 { id, text, completed } 這樣的 Todo 物件:
<ul className="todo-list">
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
</ul>
這段程式碼的執行方式是:
todos 陣列。map() 逐一讀取每筆資料。todo 代表目前這筆待辦事項。todo 都回傳一個 TodoItem。TodoItem 顯示在 <ul> 裡。其中:
todo={todo}
表示將目前這筆 todo 透過 Props 傳給 TodoItem。
假設 todos 有三筆資料,map() 就會產生三個 TodoItem Component。
因此,整段程式碼可以理解成:
將
todos裡的每一筆資料轉換成一個TodoItem。
map() 真的等於 v-for 嗎?比較兩個專案的寫法:
<!-- Vue -->
<TodoItem
v-for="todo in todos"
:key="todo.id"
:todo="todo"
/>
// React
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
/>
))}
兩者達成的結果很接近,都是逐一取得陣列資料並建立 Component。
只不過 map() 並不是 React 版本的 v-for,因為 map() 原本就是 JavaScript 的陣列方法。React 只是使用 map() 回傳的 JSX 陣列來顯示清單。
比較準確的理解是:
Vue 使用 v-for 處理清單;React 直接使用 JavaScript 的 map() 將陣列轉換成 JSX 清單。
key?使用 map() 產生清單時,每一個項目都需要一個 key:
key={todo.id}
key 可以幫助 React 辨認每個 Component 對應到陣列中的哪一筆資料。
當清單中的資料被新增、刪除或重新排序時,React 可以透過 key 判斷哪些項目發生變化,並正確更新畫面。
適合的 key 必須:
目前專案中的每一筆 Todo 都有自己的 id:
const newTodo: Todo = {
id: Date.now(),
text,
completed: false,
}
因此可以使用:
key={todo.id}
不建議在 Render 時隨機產生 key:
key={Math.random()}
因為每次 Render 都會得到不同的值,React 便無法確認這次與上次的項目是不是同一筆資料。
如果資料本身有穩定的 id,也不應優先使用陣列索引:
key={index}
因為清單被新增、刪除或重新排序時,索引可能跟著改變。
另外,key 是 React 使用的特殊屬性,不會像一般 Props 一樣傳給 TodoItem。
React 官方文件也建議使用資料中穩定且唯一的 ID 作為 key。
React 官方文件:Rendering 列表
v-for 指令,而是直接使用 JavaScript 的 map() 把陣列轉換成 JSXmap() 會逐一處理陣列中的資料,並根據每次回傳的內容建立新陣列map() 產生清單時,每個項目都需要 key
key 應該使用資料中穩定且唯一的 IDkey 是 React 的特殊屬性,不會成為一般 Props到目前為止 React 給我的感覺仍然比較像是在寫 JavaScript。
今天先安餒,既然寫了 v-for ,那下一篇沒意外就是跟 v-if 相關了