React Hooks: 使用 useImperativeHandle 來跟子元件互動

YY
6 min readSep 11, 2021

如果你對 React Hooks 是什麼還沒那麼了解的話,也許可以先看看這篇的簡易教學

什麼是 useImperativeHandle

官方文件是這麼解釋的

useImperativeHandle customizes the instance value that is exposed to parent components when using ref.

用簡單的說法就是,你可以針對一個 react component 定義他要 expose 的任何屬性

比方說一個 <CustomButton /> 可以定義一個 toggleDisabled function 屬性讓使用這個元件的人 aka 父元件可以直接呼叫這個 function 來控制 <CustomButton /> 是否 disabled

或是在 <CustomButton /> 裡會記錄 button 被按了幾次並 expose 出去,父元件就能直接取得

如下 gif,在父元件 (<App />) 中可以透過 customButtonRef.current.toggleDisable & customButtonRef.current.count 來存取 <CustomButton /> expose 出來的屬性

如何使用 useImperativeHandle

在此之前,先來看看要怎麼使用 ref 存取子元件裡的元素

如上圖有個 <CustomInput /> 包裝了 input element,若想要父元件能透過 ref 存取的這個 input 則需要用到 react ref 搭配 forwardRef 來完成

然而 useImperativehandle 也是相同原理,在子元件利用 forwardRef 與 useImperativeHandle 來 expose 自訂的 ref 屬性,在父元件就能用同樣方式取得該子元件的 ref

接著來看看 useImperativeHandle 的用法

useImperativeHandle(ref, createHandle, [deps])

useImperativeHandle should be used with forwardRef

從上方的 demo code 及官方文件應該可以很清楚的了解要如何使用,首先在元件必須透過 forwardRef 拿到 ref object 並塞入 useImperativeHandle 裡,然後定義要 expose 哪些屬性,以及用過 Hooks 一定會碰到的 dependency array

source: https://reactjs.org/docs/hooks-reference.html#useimperativehandle

使用情境

然而在什麼情況下會用到這個 Hook 呢?

試想你有一個發送 email 的系統,其中一頁可以新增模板,包含 主旨收件人副本內文 或其他更多欄位,按下儲存後要把這些欄位的值送給 server

而有另一個發送頁面,有著同樣的 email 編輯器介面與欄位,使用者輸入完或直接選擇模板後可以按下發送,但寄送前會在前端針對各欄位做驗證再把值送給 server

在這種狀況下勢必會把 email editor 的部分拆成共用元件 (只會包含相關欄位的部分,並不會有 submit button),但抉擇的時候就來了,要怎麼管理這些欄位的 state

1. 不管了,都給父元件處理,我只管 UI

於是有了這樣的 code

可以看到一個欄位就會有一個 state、prop、handler,那如果今天欄位更多了,要用到的頁面也更多,那是不是 EmailEditor 就變得沒那麼好用了

2. 那把欄位 state 包起來統一處理不就好了

於是 code 變成了這樣

變得正常且精簡許多,唯一的問題是在使用者輸入的過程中會讓父元件一直 re-render,需要注意是否會有相關影響

再者,其實父元件並不在意使用者輸入的過程,而是當按下儲存/送出時要拿到每個欄位的值

3. Email Editor 自己管理欄位 state,並 expose 出去

於是 useImperativeHandle 終於出場

首先中間欄的 EmailEditor 自己管理自己的 state,再透過 useImperativeHandle 釋出一個 values 屬性讓父元件可以存取到各欄位的值

而左欄 App 即是父元件,把 emailEditorRef 綁到 EmailEditor 上即可使用 emailEditorRef.current.values 拿到當前欄位的值,也就如同右欄輸入完欄位後按下 Send Email button 可在父元件拿到正確的值接著作後續的處理

以上,即是對 useImperativeHandle 的一點小了解,雖然操作上跟 react 資料流有點衝突,但也是有他好用之處

--

--