iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
佛心分享-IT 人職涯歷練

從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始系列 第 15 篇

Day 15|API 回來的資料不能直接用?map() 其實是在幫 UI 整理資料

  • 分享至 

  • xImage
  •  

上一篇講到巢狀資料:

data?.customer?.name

也提到一個很常見的情況:

API 回來的資料結構,不一定剛好就是 UI Component 需要的格式。

這時候前端常常會做一件事:

map()

以前我很容易把 map() 記成:

「一種迴圈。」

但實際進專案後才慢慢發現:

map() 更重要的用途,是把一份 Array 轉換成另一份新的 Array。


先看最簡單的例子

假設:

const numbers = [1, 2, 3];

我們想把每個數字乘以 2:

const result = numbers.map(number => {
  return number * 2;
});

結果:

[2, 4, 6]

流程:

原本 Array

[1, 2, 3]

↓ map()

1 → 2
2 → 4
3 → 6

↓ 新 Array

[2, 4, 6]

所以 map() 的重點不是單純:

每一筆都跑一次。

而是:

每一筆經過處理後,產生一個新的結果,最後組成新的 Array。


API 資料通常不會剛好長成 UI 要的樣子

假設 API 回來:

const customers = [
  {
    id: 1,
    customer: {
      name: "ABC Company",
    },
  },
  {
    id: 2,
    customer: {
      name: "XYZ Company",
    },
  },
];

但今天 Select 元件需要的是:

[
  {
    label: "ABC Company",
    value: 1,
  },
  {
    label: "XYZ Company",
    value: 2,
  },
];

兩份資料其實有同樣的內容。

只是:

形狀不同。

API 給的是:

id
customer
 └─ name

Select 想要的是:

label
value

這時候就需要轉換。


用 map() 把 API 資料整理成 UI 要的格式

例如:

const options = customers.map(item => ({
  label: item.customer?.name ?? "N/A",
  value: item.id,
}));

最後:

options

會變成:

[
  {
    label: "ABC Company",
    value: 1,
  },
  {
    label: "XYZ Company",
    value: 2,
  },
];

流程:

API Response
↓
customers

每一筆 item
↓
取 item.customer.name
取 item.id
↓
重新組成

{
  label,
  value
}

↓
新的 Array
↓
Select options

這就是我後來理解 map() 最有感的地方。


item => ({ ... }) 又是在幹嘛?

這段:

customers.map(item => ({
  label: item.customer?.name ?? "N/A",
  value: item.id,
}));

第一次看很像一坨。

可以先拆成完整版本:

const options = customers.map(item => {
  return {
    label: item.customer?.name ?? "N/A",
    value: item.id,
  };
});

流程:

拿到一筆 item
↓
return 一個新的 Object
↓
下一筆
↓
再 return 新 Object
↓
最後全部組成 Array

所以:

item

是:

原本 Array 裡目前正在處理的那一筆資料。

而:

return {
  label: ...,
  value: ...,
}

就是:

這一筆轉換後,我希望它長什麼樣子。


為什麼括號裡直接寫 {}?

這裡還有一個很容易讓我混亂的語法:

item => ({
  label: item.name,
  value: item.id,
})

為什麼 Object 外面要多一層:

(...)

因為箭頭函式如果直接寫:

item => {
  ...
}

JavaScript 會把 {} 理解成 function body。

所以如果想直接 return Object:

item => ({
  ...
})

就是用括號告訴 JavaScript:

這整包 {} 是我要回傳的 Object。

等同:

item => {
  return {
    ...
  };
}

map() 不會直接改原本的 Array

例如:

const customers = [
  {
    id: 1,
    name: "ABC",
  },
];

接著:

const options = customers.map(item => ({
  label: item.name,
  value: item.id,
}));

此時:

customers

仍然是:

[
  {
    id: 1,
    name: "ABC",
  },
]

而:

options

才是新的:

[
  {
    label: "ABC",
    value: 1,
  },
]

所以:

customers
→ 原始資料

options
→ map() 後的新資料

這也是 map() 很重要的特性:

它會回傳新的 Array。


這跟 forEach() 有什麼差別?

這也是我以前很容易混在一起的地方。

假設:

const numbers = [1, 2, 3];

map()

const result = numbers.map(number => {
  return number * 2;
});

得到:

[2, 4, 6]

因為 map() 的目的通常是:

把每一筆轉換後,產生新的 Array。


forEach()

numbers.forEach(number => {
  console.log(number);
});

這種比較像:

每一筆都幫我做某件事。

例如:

印 log
修改 DOM
呼叫 function

但它不會像 map() 一樣,自動幫我們收集成新的 Array。

所以可以先簡單分:

map()
→ 我想把 Array 轉成另一個 Array

forEach()
→ 我想對每一筆執行某件事

React 裡為什麼也一直看到 map()?

上一篇以前其實就看過:

memos.map(memo => (
  <li>{memo}</li>
))

這裡本質上也是一樣的。

原本:

["買牛奶", "寫文章"]

經過:

map()

變成:

[
  <li>買牛奶</li>,
  <li>寫文章</li>,
]

也就是:

資料 Array
↓
map()
↓
React Element Array
↓
Render

所以 React 並不是賦予 map() 什麼特殊能力。

map() 本來就是 JavaScript Array 的方法。

React 只是很常拿它來:

把資料轉成畫面。


實務上不只 Select,Table 也很常需要整理資料

假設 API 回:

[
  {
    id: 1,
    customer: {
      name: "ABC",
    },
    amount: 1000,
  }
]

但畫面希望使用:

[
  {
    key: 1,
    customerName: "ABC",
    displayAmount: "$1,000",
  }
]

也可能:

const tableData = data.map(item => ({
  key: item.id,
  customerName:
    item.customer?.name ?? "N/A",
  displayAmount:
    `$${item.amount}`,
}));

所以資料流是:

API Response
↓
原始資料
↓
map()
↓
整理欄位
↓
UI Data
↓
Table

這種「資料轉換層」在前端其實非常常見。


但也不要看到資料就先 map()

這也是我後來慢慢學到的。

假設 API 已經回:

[
  {
    label: "ABC",
    value: 1,
  }
]

而 Select 剛好就需要:

label
value

那根本不用再:

map()

重新做一份一模一樣的資料。

所以要先問:

API 的資料形狀,跟 UI 需要的形狀差在哪裡?

真的需要轉換,才轉。


我現在看到 map(),會先問三件事

以前:

map() 又是什麼語法?

現在我會先問:

① 原本 Array 長什麼樣子?

② 每一筆被轉成什麼?

③ 最後的新 Array 要給誰使用?

例如:

const options = customers.map(item => ({
  label: item.customer?.name ?? "N/A",
  value: item.id,
}));

就變成:

原始資料
customers

↓ map()

每一筆:
{
  id,
  customer
}

↓ 轉換成

{
  label,
  value
}

↓ 最後

options

↓
Select

只要找到這三件事,map() 就不會再只是一個抽象的「迴圈」。


今天真正想記的是:前端常常是在做「翻譯」

API 關心的可能是:

資料庫 / Domain 的資料結構

UI Component 關心的可能是:

我要 label / value

所以 Frontend 中間常常需要做:

API Data
↓
Transform
↓
UI Data

而 map() 就是這種資料轉換裡很常見的工具。

所以我現在看到:

map()

不會只想:

它在跑迴圈。

而會多問一句:

它正在把什麼資料,轉成什麼形狀?

下一篇可以繼續接資料轉換裡另一個非常常見的問題:

為什麼有時候要用 { ...data }、[...items]?Spread Operator 到底是在「展開」什麼?


上一篇
Day 14|data.customer.name 為什麼會突然噴錯?巢狀資料到底在防什麼?
下一篇
Day 16|... 到底是在展開什麼?Spread Operator 一次看懂
系列文
從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言