在 React 裡,父元件通常 無法直接控制子元件的內部狀態或行為。但有時候,我們希望 父元件可以透過某些方法操作子元件,例如:重置狀態、讀取資料或觸發某些事件——這時候,useImperativeHandle
就能派上用場。
這篇文章將透過一個「自動販賣機」的範例,一步步了解:
- 什麼是
useImperativeHandle
- 它為什麼需要搭配 forwardRef
- 如何在實務中運用它來建立一個「可由父元件遙控的子元件」
💡 什麼是 useImperativeHandle
useImperativeHandle
是一個 React Hook,讓你可以 自訂一個 ref 對應的物件內容。這樣父元件在透過 ref 取得子元件時,不會拿到整個 DOM 或元件實例,而是取得你「想要暴露出去的功能」。
它通常要和 forwardRef
一起使用,才能讓函式型元件支援 ref。
🧪 範例介紹:一台自動販賣機
假設我們有一個 VendingMachine 元件,他有自己的內部狀態,包含已選擇的商品、投入的金額等等。但通常來說,我們還會需要一些其他的功能,例如客人發現他選錯飲料了,想要取消購買;或是販賣機異常了,工程師來維修的時候想要重置整個飲料機的狀態,這兩個行為都會牽涉到 VendingMachine 的內部狀態,但因為 這些操作是在父元件中觸發的行為,子元件自己無法預測,也不應該自行處理。
這樣做有幾個好處:
- 保持封裝性:子元件仍然控制自己的 state,外部只能透過「指定的介面」來操作它。
- 提供明確 API:讓元件的使用者知道有哪些功能可以用,不會亂操作。
- 應付複雜交互邏輯:像是「取消交易」、「重置系統」這類跨元件操作就能被妥善處理。
因此,在我們的自動販賣機範例中,我們選擇將以下三個功能透過 useImperativeHandle
提供給外部元件使用:
cancelSelection
: 客人取消交易resetMachine
: 工程師重置機器getCurrentStatus
: 查詢目前販賣機的狀態
下一步就讓我們來看要怎麼使用 useImperativeHandle
吧。
👷 建立可控制的子元件:VendingMachine
第一步:使用 forwardRef
包裝元件
|
|
第二步:定義要暴露給外部的功能
|
|
👨🔧 父元件:透過 ref 呼叫子元件方法
第三步:使用 useRef
建立 ref
|
|
第四步:在父元件中呼叫子元件方法
|
|
✅ useImperativeHandle
的應用情境
這種做法特別適合用在:
- 表單控制:例如讓父元件觸發表單驗證、重置欄位
- 外部控制元件:像 modal、canvas、圖表、音樂播放器等需要控制的 UI
- 設計可複用元件 API:把內部邏輯包在元件中,只暴露必要的 API 給使用者
不過要注意的是,useImperativeHandle
不會讓你繞過 React 的「單向資料流」原則,因為你只能操作子元件「允許」的部分。
若父元件可以直接控制全部狀態,那就不需要這個 hook,只有在子元件內部邏輯複雜、但又需要提供部分控制給外部時才用得到。
📎 結語
useImperativeHandle
在實務中雖然使用頻率不高,但在處理一些進階的互動場景(例如這個自動販賣機)時,可以幫你建立乾淨又可控制的元件 API,讓元件設計更彈性、更模組化。
希望你喜歡今天的文章,我們下次再見 👋🏻