iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Software Development

文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念系列 第 9 篇

Day 9:狀態管理的演進——從局部狀態(Local State)到全域狀態(Global Store)

  • 分享至 

  • xImage
  •  

在掌握了單向資料流的基礎後,我們開始面對另一個棘手的挑戰:狀態應該存放在哪裡?剛開始,我們傾向於將所有狀態放在組件內部(Local State),但隨著代購 App 的功能日益複雜,從個人化的瀏覽紀錄到跨頁面的購物車狀態,這類資訊若僅止於局部組件,將引發難以維護的「屬性鑽取(Prop Drilling)」問題。這篇要談的是從局部狀態跨越至全域狀態(Global Store)的演進邏輯,以及如何決定資料的「居住地」。

一、局部狀態(Local State)

定義:僅限於特定組件或其子組件內部使用的資料。

特點:

  1. 封裝性高:變更不會影響外部,風險小。
  2. 生命週期短:組件卸載即消失。
  3. 適用場景:表單輸入框、按鈕開關、選單展開狀態等純 UI 狀態。

二、屬性鑽取(Prop Drilling)的困境

當多個距離遙遠的組件需要共用同一份狀態時,我們被迫將狀態層層向下傳遞。這會導致中間不相關的組件必須處理這些資料,導致程式碼冗餘且極難維護。

三、全域狀態(Global Store)

定義:將核心資料抽離出組件樹,存放在一個外部的儲存空間(Store)。

機制:任何組件皆可透過特定的 API 訂閱或讀取該狀態,實現狀態的「隨需取用」。

昨天講的規則——狀態只能透過 reducer、action 這條路徑被修改——並不會因為狀態搬到全域就改變,只是原本「住在一個元件裡」的狀態,現在「住在一個大家都能訂閱的地方」,管理它的規則還是同一套單向資料流。

四、代購 App 案例:狀態的居住地

Local State 應用:商品詳情頁中的「文字輸入框」或「展開折疊規格說明」。這些資訊僅對該商品頁有意義,不需要同步給其他頁面,存放在組件內部即可。

Global Store 應用:購物車(Cart)。購物車內容需要出現在商品瀏覽頁(顯示購物車圖示數字)、結帳頁(計算總價)、訂單確認頁。若把購物車狀態寫在局部組件,就得靠一層層傳遞把資料送達——實際會長這樣:

// 屬性鑽取:購物車數字要一路往下傳,中間元件其實用不到
function App() {
  const [cartCount, setCartCount] = useState(0);
  return <Layout cartCount={cartCount} />;
}

function Layout({ cartCount }) {
  // Layout 自己根本用不到 cartCount,只是幫忙轉手
  return <Header cartCount={cartCount} />;
}

function Header({ cartCount }) {
  // 傳到這裡才終於用得到
  return <CartIcon count={cartCount} />;
}

改用全域狀態之後:

// 全域狀態:誰需要誰自己拿,不用一層層傳
const CartContext = createContext();

function App() {
  const [cartCount, setCartCount] = useState(0);
  return (
    <CartContext.Provider value={{ cartCount, setCartCount }}>
      <Layout />
    </CartContext.Provider>
  );
}

function Layout() {
  return <Header />; // 不用再接 cartCount 這個路人參數
}

function Header() {
  return <CartIcon />;
}

function CartIcon() {
  const { cartCount } = useContext(CartContext); // 直接拿
  return <span>{cartCount}</span>;
}

Layout 跟 Header 兩層完全不用再知道 cartCount 這個參數的存在,只有真正需要它的 CartIcon 會去訂閱。這就是為什麼購物車這種跨頁面共用的狀態,適合放進 Global Store,而不是一路用 props 傳下去。

五、結論

狀態管理的演進,本質上是在「隔離性」與「共享性」之間尋找平衡。局部狀態保證了組件的獨立與乾淨;全域狀態則解決了大規模應用的資料同步問題。理解這兩者的界線,是開發健壯系統的關鍵。剛開始搞不懂局部狀態和全域狀態差在哪,後來把它想成兩種不同的視角就懂了——局部狀態是用微觀去看,只在乎眼前這個元件自己的事;全域狀態則是用宏觀視角去理解整個資料架構,看的是這份資料在整個系統裡怎麼被多個地方共用。同一份購物車資料,如果只用微觀視角想「這個元件需要它」,就會很自然地想用 props 一路傳下去;只有切換到宏觀視角,才會意識到這份資料的歸屬,根本不該綁在任何一個元件身上。


上一篇
Day 8:單向資料流(Unidirectional Data Flow)——為什麼雙向綁定容易引發狀態失控?
下一篇
Day 10:快取與持久化策略——LocalStorage、Session 與記憶體快取的生命週期管理
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 13 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言