上一篇將漫畫與動畫收藏清單的需求交給 AI 後,它沒有把所有程式碼都寫在 App.tsx,而是拆成了好幾個 Component:
App
├─ PageHeader
├─ AddItemForm
└─ CollectionList
└─ CollectionItem
一開始可能會覺得說畫面明明沒有多複雜,真的需要拆成這麼多檔案嗎?
如果全部寫在 App.tsx,應該也能做出一樣的東西吧?既然自己看不太懂 AI 拆分的邏輯,那就是先停止瞎猜,直接問它:
為什麼要拆成
PageHeader、AddItemForm、CollectionList和CollectionItem?請分別說明拆分的原因。哪些 Component 適合獨立?哪些其實也可以留在App.tsx?先不要修改程式碼。
這次先讓 AI 講理由,而不是看到它拆好就直接接受。
App 負責管理整個收藏清單從 AI 給的回答來看,App 就是整個頁面的管理者。
目前收藏資料的 State 放在 App.tsx:
const [items, setItems] = useState<CollectionEntry[]>(loadItems)
新增、修改狀態和刪除作品的函式也都放在這裡,再透過 Props 交給其他 Component:
<PageHeader totalCount={items.length} />
<AddItemForm onAdd={addItem} />
<CollectionList
items={items}
onStatusChange={updateStatus}
onDelete={deleteItem}
/>
如果連頁面標題、完整表單和每一筆收藏的 JSX 全部塞進來,App.tsx 就會長成一大串。
拆開之後至少可以先從這幾行看出頁面大概長怎樣,App 就專心處理整個專案共用的收藏資料就好,不用什麼都攬在自己身上。
AddItemForm 負責新增作品AddItemForm 包含作品名稱、類型與觀看狀態,也有自己的 State:
const [title, setTitle] = useState('')
const [type, setType] = useState<MediaType>('manga')
const [status, setStatus] = useState<WatchStatus>('want')
送出表單後,它會透過 onAdd 把整理好的資料丟給 App。
這個 Component 不是只有一小段畫面而已,還包著輸入資料跟處理表單的整套流程。看到 AddItemForm 這個名字,就知道要改新增功能該往哪個檔案找,不用大海撈針。
收藏清單又被拆成:
CollectionList
└─ CollectionItem
CollectionList 負責接收整個 items 陣列,判斷目前是否有收藏資料,再用 map() 產生每一個 CollectionItem:
{items.map((item) => (
<CollectionItem
key={item.id}
item={item}
onStatusChange={(status) =>
onStatusChange(item.id, status)
}
onDelete={() => onDelete(item.id)}
/>
))}
CollectionItem 則負責顯示單筆作品,包括作品類型、名稱、觀看狀態跟刪除按鈕。
這個拆法跟之前 Todo List 裡的 TodoList、TodoItem 可以說是同一套:
CollectionList:管整份清單CollectionItem:管其中一筆資料每一筆作品都會重複用到相同的畫面結構,所以把單筆作品獨立出來確實比較好理解,用比喻來說就是一個大型布告欄裡,每新增一個東西就會出現一張卡片。
PageHeader 也一定要拆嗎?PageHeader 只負責顯示頁面標題和收藏數量:
<PageHeader totalCount={items.length} />
跟表單、收藏清單比起來,它現在的內容單純很多,也沒有自己的 State。
AI 的判斷是,拆出來可以讓 App 的頁面結構更清楚,但以目前只是用一個練習用的小型專案規模來看,其實也能直接把 Header 的 JSX 留在 App.tsx 裡。
所以 PageHeader 感覺不是非拆不可,比較像是一種整理習慣,拆不拆就看需求而已。
以結論來說,Component 怎麼拆沒有標準答案,有些部分很明顯適合獨立,有些就真的要看專案大小跟自己看得順不順眼了。
當然也不是每一小段 JSX 都要獨立成 Component。
例如,如果再把 CollectionItem 繼續拆成:
TypeBadge
ItemTitle
StatusSelect
DeleteButton
這些東西目前內容不多,也只有一個地方在用,拆開後反而要一直切換檔案找東西,還可能多出一堆 Props,不見得比較好懂,感覺反而更麻煩。
目前我先從幾個方向判斷要不要拆:
Component 不一定要重複使用才值得拆分,像 AddItemForm 雖然目前只出現一次,但因為它有完整的表單功能,獨立出來讀起來算是輕鬆不少。
React 官方文件也提到,一個 Component 理想上可以先專注做一件事,等它逐漸變大時,再繼續拆成比較小的 Component。
React 官方文件:React 的思考模式
以練習來說,當然可以看個人決定。你可以直接全部接受,也可以看過、理解程式內容後,再決定哪些部分需要更改。最重要的是確認自己真的有理解程式碼在寫什麼,也要知道過程中 AI 到底幫你改了什麼、刪了什麼。
針對第一次生成的內容,我自己大概的判斷是:
AddItemForm:有自己的 State 與表單流程,保留CollectionList:負責處理整份陣列與空白狀態,保留CollectionItem:每筆作品會重複使用,保留PageHeader:不一定需要獨立,但拆開也不會造成什麼負擔所以這次先不動 AI 原本的 Component 結構。
不修改不是因為 AI 一定是對的,而是確認它的理由、再自己看過實際程式碼之後,覺得目前這樣拆還算合理,沒必要為改而改。
這次協作也沒生出什麼新程式碼,單純是我對看不懂的結構提出問題,讓 AI 說明設計理由,再自己判斷要不要買單。
App 負責管理主要的收藏資料與操作函式AddItemForm 負責新增作品的表單流程CollectionList 負責整份清單,CollectionItem 負責單筆作品PageHeader 拆不拆沒有標準答案先這樣,下篇見!