
一句話:小專案隨便寫都能動,但元件超過 50 個的時候,架構決定你是在寫 code 還是在還債。
先講結論
React 的架構問題歸納起來就兩件事:元件怎麼分和狀態放哪裡。這篇不是 React 入門(如果你需要,先看 React Hooks),而是回答一個問題:你已經會寫 React 了,怎麼寫出不會讓三個月後的自己想翻桌的 React 應用?
元件分層:問自己三個問題
元件分層不是什麼高深的理論,就是問三個問題:
- 這個元件換到另一個專案還能用嗎?→ UI 層(Button、Modal、Table)
- 換到另一個頁面還能用嗎?→ Features 層(UserTable、UserForm)
- 都不行?→ Pages 層(UserListPage)
就這樣。UI 層不知道業務邏輯,Features 層不知道 URL,Pages 層負責把它們組合起來。
你有沒有遇過一個 component 裡面同時 fetch 資料、處理表單、控制 modal、還寫了一堆 CSS?那就是沒有分層的症狀。我之前寫過一個 800 行的 component,重構的時候差點哭出來。
Container / Presentational 分離
Hooks 出來之後很多人說這個 pattern 過時了,但分離邏輯和呈現的思路永遠不過時:
// Container:負責邏輯
function UserListContainer() {
const { users, loading, error } = useUsers();
const handleDelete = useDeleteUser();
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <UserList users={users} onDelete={handleDelete} />;
}
// Presentational:負責呈現
function UserList({ users, onDelete }) {
return (
<ul>
{users.map(user => (
<UserCard key={user.id} user={user} onDelete={onDelete} />
))}
</ul>
);
}為什麼要這樣分?Presentational 元件可以用 Storybook 獨立開發和測試,同一份資料可以有 list / grid / table 三種呈現方式。邏輯改了不用動 UI,UI 改了不用動邏輯。
Compound Components
當一組元件需要共享隱含的狀態時——比如 Tabs、Accordion、Dropdown:
<Tabs defaultValue="profile">
<Tabs.List>
<Tabs.Trigger value="profile">Profile</Tabs.Trigger>
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="profile">Profile content</Tabs.Content>
<Tabs.Content value="settings">Settings content</Tabs.Content>
</Tabs>父元件透過 Context 共享狀態,子元件可以任意組合。使用者寫起來是宣告式的,不用自己管 active state。如果你用過 Radix UI 或 Headless UI,你已經在用 Compound Components 了。
狀態管理:先問「這個狀態住在哪裡最自然」
狀態管理的問題不是「該用什麼工具」,而是「這個狀態屬於誰」。
Local State——只有一個元件用。useState / useReducer,不要想太多。表單輸入、Modal 開關、tooltip 顯示,都是 local state。
Server State——來自 API。這個最容易搞錯。很多人把 API 回傳的資料放進 Redux / Context,然後手動管理 loading、error、refetch、cache invalidation… 拜託不要。
function UserList() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => api.getUsers(),
});
// React Query 自動處理 loading、cache、background refetch、retry
// 你不用寫任何一行狀態管理的 code
}URL State——搜尋條件、分頁、篩選器。能放 URL 就放 URL,使用者可以分享連結、可以按上一頁。這些狀態放在 Redux 裡是浪費。
Global State——以上都不是的,才考慮全域狀態。2026 年了,真正需要全域管理的狀態其實很少:
- 使用者登入狀態
- 主題 / 語言偏好
- 通知佇列
- 多步驟表單的跨頁資料
就這些。如果你發現你的 Redux store 有 20 個 slice,我敢說其中 15 個應該是 React Query 或 URL state。2016 年全部塞 Redux 的風潮,真的害了不少人。
元件怎麼分、狀態放哪裡,搞定這兩件事,你的 React 應用就不會長成毛線球。下一篇講 Custom Hooks 設計、效能優化、和專案結構——也就是「骨架有了,怎麼讓肌肉長對地方」。
系列文章
- (一)元件設計與狀態管理(本篇)
- (二)Custom Hooks、效能與專案結構