大型 React 專案中 API 層的演進與分層設計
🤞 完整範例:Github Repo │ 👻 Live Demo:點我看 demo 專案被擴充得越來越大時,發現自己埋的坑越來越多。 為了讓示範更清晰,本文先略過 TypeScript。 前期 API 的呼叫方式 前期開發 React 專案時,我習慣將 API 請求統一集中在資料夾 /api 下,由每個 component 單獨呼叫使用。 專案規模擴大後出現的架構問題 隨著專案的規模愈來愈大,功能愈來愈複雜時,同一個 API 在多處被呼叫,我開始碰到以下問題: 每個 component 需各自維護 loading / error / data 等狀態,導致狀態管理分散、代碼冗餘 同一支 API 被多處重複呼叫,導致請求重複,造成不必要的 Network、後端壓力及 UX 延遲 資料 cache 的缺失,不同 component 中拿到的資料內容可能出現不一致,需額外處理 資料轉換邏輯分散,難以維護 錯誤處理容易不一致,難以維護,使用者體驗不穩定 跨 component 的資料共用困難,狀態同步成本高,可能需要不斷地通過 props 傳遞或使用 store ⋯⋯ 架構的重構:API / Service / Hook / Component 因此,我決定重新設計資料存取的架構。將原本由 component 直接呼叫 API 並處理所有相關邏輯的做法,依責任拆分為 API 層、Service 層、Hooks 層與 Component 層。透過職責分離,讓每一層專注於單一責任,進而提升整體的可讀性、可維護性與可擴充性。 ...
從 Array.map() 的一對一轉換,到用 Map 建立索引
拖了好久的文,終於來寫。 這篇預設給熟悉Array.map()與for loop的朋朋閱讀。 什麼情境下選擇改用Map 通常在從 server API 取得資料後、實際應用到前端之前,我會先進行資料轉換,調整成自己習慣的 key 命名及資料格式,並重組成更適合使用的資料結構。 當需求只是單純的一對一轉換時,整個流程前後都是以 array 為主,使用Array.map()就能很好地完成這件事。 不過當需求開始變成「一個 key 對應多筆資料」時,資料處理的重點變成「如何依照 key 累積資料」。這時我發現,使用 object 來暫存分組結果,並不能清楚表達我正在建立一個用來查找與分組的索引結構,因此改用Map,讓 code 本身更貼近實際的資料處理意圖。 當資料是一對一時:Array.map() 當從 server API 取得資料後,如果需求只是將資料轉換成前端習慣的格式,例如調整 key 命名、轉換欄位值或補上預設值,這類一對一的資料轉換,使用Array.map()就已經非常足夠。 例如,server 回傳的員工資料可能長這樣: const employees = [{ "eid": "202242", // 員工編號 "did": "d01", // 部門 ID "fullname": "Tom Smith", // 員工全名 "gender": 1 // 員工性別 }, { "eid": "202414", "did": "d02", "fullname": "Chen Peiyu", "gender": 2 }, { "eid": "202505", "did": "d01", "fullname": "Chang lin", "gender": 2 }]; 在前端實際使用前,我通常會先做一次資料轉換: const genderMap = { 1: "male", 2: "female" }; const updatedEmployees = employees.map((emp) => ({ "employeeId": emp.eid, "departmentId": emp.did, "fullname": emp.fullname, "gender": genderMap[emp.gender] ?? "unknown" })); 在這種情境下,每一筆輸入資料都對應到一筆輸出資料,資料筆數前後保持一致,Array.map()能很清楚地表達「這一筆資料要被轉換成什麼樣子」。 也正因為如此,當需求僅僅是 key 的重命名或資料格式的調整時,我認為Array.map()是最直覺、合適的做法,完全不需要額外的狀態或暫存結構。 ...