Photo by Lautaro Andreani on Unsplash
前言
因為身體關係,所以暫離了前端領域一段時間;而近期隨著身體已經趨於穩定(恢復中),所以想再度接回前端的軌道,目前保持進修中,也參加了六角開的 React 入門工作坊,增加與前端的連結。
先前寫過 Vue 一段時間,所以在學習 React 時,能比較知道這兩者的一些比對,React 寫法的確比較自由,但很吃原生 JS 的功力(這方面偏弱 QQ),而這篇文章主要是想把第一週作業的過程,做點紀錄跟刻意練習寫程式的思路。
作業放置處
大致的流程為:
- 定義資料並渲染
- 調整庫存數量
- 庫存數量優化
- 編輯品項名稱
定義資料並渲染
不論使用哪個前端框架,我們都不是用 Dom 的角度做切入;而是採用資料驅動畫面的概念,所以定義資料、操作資料都是很常見的。
使用 JSX 語法上,需要習慣一段時間。分享我自己的小撇步:在 JSX 語法中,如果要開始寫 JS 就用 {};而準備要寫 HTML 就用 ()。(註:這是一個記憶撇步,實際上 JSX 內的概念就都是 JS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const { useState } = React;
const drinkMenu = [ ];
function MenuManage() { const [menu, setMenu] = useState(drinkMenu);
return ( <div className="container"> <h1>餐點管理工具</h1> <table> // ...略 <tbody> {menu.map((item) => { return ( <tr key={item.id}> <td>{item.name}</td> <td> <small>{item.content}</small> </td> <td>{item.price}</td> <td>{item.qty}</td> </tr> ); })} </tbody> </table> </div> ); }
const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<MenuManage />);
|
調整庫存數量
(考量到程式碼的長度,所以有些部分會做個省略)
再來我們要調整庫存數量,所以做了兩個按鈕來對應增減的功能,而像是 () => handleQtyAdd(item.id)
所傳入的 item.id
則為識別用,判斷我們要改的是資料中的哪一個。
至於在庫存增加跟減少的功能中,我們得產出一組新的 array,然後再做 setState (下方範例是 setMenu),是因為 React state immutable 的設計概念。簡單來說就是寫 React 遇到要更新物件或陣列時,要根據更新需求去產新的物件或陣列。(有興趣的話可以 Google,或者參考這篇文章)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
function MenuManage() { const [menu, setMenu] = useState(drinkMenu);
const handleQtyAdd = (id) => { const menuTemp = menu.map((item) => { return item.id === id ? { ...item, qty: (item.qty += 1) } : item; }); setMenu(menuTemp); };
const handleQtyMinus = (id) => { const menuTemp = menu.map((item) => { return item.id === id ? { ...item, qty: (item.qty -= 1) } : item; }); setMenu(menuTemp); };
return ( <div className="container"> <h1>餐點管理工具</h1> <table> // 略... <tbody> {menu.map((item) => { return ( <tr key={item.id}> // 略... <td> <button type="button" onClick={() => handleQtyMinus(item.id)}> - </button> {item.qty} <button type="button" onClick={() => handleQtyAdd(item.id)}> + </button> </td> </tr> ); })} </tbody> </table> </div> ); }
const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<MenuManage />);
|
如果不太理解三元運算子,或寫起來很卡的話,也可以先用最簡易的 if else 來寫,然後再慢慢對照三元運算的概念。
以下取庫存增加的 function 來做範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const handleQtyAdd = (id) => { const menuTemp = menu.map((item) => { if (item.id === id) { return { ...item, qty: (item.qty += 1), }; } else { return item; }
}); setMenu(menuTemp); };
|
庫存數量優化
因為庫存數量增減的 function 只有差異在 item.qty += 1
跟 item.qty -= 1
,所以把增減的兩個 function 縮減成一個,然後多傳入一個參數(type) 作為增減的判斷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| const { useState } = React;
const drinkMenu = [ ];
function MenuManage() { const [menu, setMenu] = useState(drinkMenu);
const handleQtyFn = (id, type) => { const menuTemp = menu.map((item) => { return item.id === id ? { ...item, qty: type === "add" ? (item.qty += 1) : (item.qty -= 1) } : item; }); setMenu(menuTemp); };
return ( <div className="container"> <h1>餐點管理工具</h1> <table> // 略 ... <tbody> {menu.map((item) => { return ( <tr key={item.id}> // 略... <td> <button type="button" onClick={() => handleQtyFn(item.id, "minus")} > - </button> {item.qty} <button type="button" onClick={() => handleQtyFn(item.id, "add")} > + </button> </td> </tr> ); })} </tbody> </table> </div> ); }
const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<MenuManage />);
|
編輯品項名稱
這個部分剛開始有卡關一陣,主要是定義 state 資料所產生的問題。在編輯品項中,剛開始的粗淺想法是增加一個 boolen state 來做判斷 (像是:const [status, setStatus] = useState(true)
),不過這樣的方式無法去識別點編輯的項目是哪一個,例如對某個品項點擊編輯,會發現全部品項都可以編譯。
後來改用定義 id(const [currentId, setCurrentId] = useState('')
)的方式為依據。流程大致是:
在初始狀態 currentId
為空值時,每個餐點項目都是顯示”品項名稱”跟”編輯按鈕”;假設第二個品項中的編輯按鈕被點擊時,就會透過 handleCurrentId
以及第二個品項的 item.id
參數去做 setCurrentId
,此時在樣板的 item.id === currentId
判斷中,第二個品項就會顯示 “input” 跟”完成按鈕”。
(至於 input 的 onChange,邏輯上跟前面庫存的寫法一致,所以就不再贅述。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| const { useState } = React;
const drinkMenu = [ ];
function MenuManage() { const [menu, setMenu] = useState(drinkMenu); const [currentId, setCurrentId] = useState("");
const handleCurrentId = (id, type) => { type === "edit" ? setCurrentId(id) : setCurrentId(""); };
const handleChangeInput = (e, id) => { const menuTemp = menu.map((item) => { return item.id === id ? { ...item, name: e.target.value } : item; }); setMenu(menuTemp); };
return ( <div className="container"> <h1>餐點管理工具</h1> <table> // 略 ... <tbody> {menu.map((item) => { return ( <tr key={item.id}> <td> {item.id === currentId ? ( <> <input type="text" value={item.name} onChange={(e) => handleChangeInput(e, item.id)} /> <button type="button" onClick={() => handleCurrentId(item.id, "complete")} > 完成 </button> </> ) : ( <> {item.name} <button type="button" onClick={() => handleCurrentId(item.id, "edit")} > 編輯 </button> </> )} </td> // 略... </tr> ); })} </tbody> </table> </div> ); }
const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<MenuManage />);
|
結尾
實際在寫 React 的時候,還是能感受到自己原生 JS 功力比較弱,所以這樣的刻意練習,應該也是會有幫助的。另外如果有什麼部分或概念寫錯,也再麻煩指導,感謝!