7-5 Uncontrolled components 和 useRef 的使用
本單元對應的專案分支為:uncontrolled-components-with-use-ref。
#
單元核心這個單元的主要目標包含:
- 了解什麼是 Uncontrolled Components
- 透過 useRef 來取得表單資料
在上一個單元中,提到了表單的兩種處理方式,分別是 controlled components 和 uncontrolled components。在上一個單元中已經說明了 controlled components 的做法,在這個單元中進一步來看如何使用 uncontrolled components 達到相同的結果。
這個單元的 useRef 的說明並不會放入最後完整的程式碼中,主要是作為示範說明用。
#
為什麼要使用 Uncontrolled Components一般來說除非是 <input type="file">
這種檔案上傳的欄位之外,多會使用 Controlled Component 來做。
但有些時候可能只是想要很簡單的去取得表單中某個欄位的值,或者是有一些情況下需要直接對 DOM 元素進行操作(例如,音樂播放器中有許多方法是直接綁在 <video>
元素上的),這時就可以使用 React 中提供的 useRef
這個 Hooks。
前一個單元在 Controlled Components 的表單中,會使用 useState
搭配 onChange
來隨時更新使用者目前在表單中填入的資料,把茲這些資料放到 React 元件內部的 state 來管理。
但在 Uncontrolled Components 並不會把資料交給 React 管理,而是自己選到該 <input />
元素後去從該 DOM 元素中把值取出來。在 React 中若想要選取到某一元素時,就可以使用 useRef
這個 React Hooks。
這裡我們就同樣以 WeatherSetting 這個表單為例,只是這次把它作為 Uncontrolled Components 搭配 useRef
來使用。
#
useRef 的基本用法useRef
的基本用法如下:
- 在
useRef
內可以放進一個預設值(initialValue) useRef
會回傳一個物件(refContainer),這個物件中會有一個名為current
的屬性,裡面放的會是一開始給的預設值- 這個物件最重要的是它不會隨著每一次畫面重新轉譯而指稱到不同的物件,而是可以一直指稱到同一個物件
你可以把透過 useRef
取得的物件,當作是完全獨立於 React 元件的變數,它不會受到 React 元件重新轉譯的影響,同樣的,當你對裡面的值進行操作時,和 useState
的 setSomething
不同,useRef
也不會觸發 React 元件重新轉譯。舉例來說:
如果是要把 useRef
當成 document.querySelector
來選取到某一元素的話,可以在該 HTML 元素上使用 ref
屬性,並把 useRef
回傳的物件放進去即可,例如:
這時候只要透過 inputRef.current
就可以指稱到 <input />
這個 HTML 元素,就很像是用 document.querySelector('input')
後取得的結果。
#
套用到設定頁面讓我們把原本 Controlled Components 的寫法移除,實際套用 Uncontrolled Components 到地區設定頁面來看看可以怎麼用:
- 先從
react
中取出useRef
這個 Hook 來用,把原本用的useState
移除
- 使用
useRef
來建立可以一直被參照到的物件,將這個回傳的物件取名為inputLocationRef
- 在
<input>
的地方,不需要再使用onChange
事件隨時更新 React 的資料狀態,而是透過ref={inputLocationRef}
讓inputLocationRef.current
可以指稱到這個 input 欄位,資料是保存在瀏覽器本身的 input 欄位中
- 定義
handleSave
方法,在該方法中,即可透過inputLocationRef.current
即可取得剛剛透過ref
指稱的元素,並且透過inputLocationRef.current.value
就可以取得該欄位的值
此時當使用者按下儲存按鈕時,一樣會在瀏覽器的 console 面板中出現使用者想要儲存的地區資訊。
#
在 Uncontrolled Components 中設定預設值對於 Uncontrolled Components 若想要定義預設值,可以在 <input>
欄位中使用 defaultValue
:
如此,當使用者一進來設定頁面時,顯示的選項就會是「臺南市」。此外,由於現在並沒有監控 onChange
事件,也沒有使用 setSomething
的方法來變更 React 元件內的資料狀態,因此不管使用者在中途切換過什麼選項,開發者都不會理會,只要在最後按下儲存按鈕時,才會從該 HTML 元素中把值取出。
提示
你可以發現當我們把 useRef
回傳的物件透過 rel
的方式放到 HTML 元素中時,就很像是用 document.querySelector
去選到該元素後,保存在 useRef
回傳物件的 current
屬性內。
#
換你了!使用 uncontrolled components 的方式取得使用者欲儲存的地區資訊這個單元主要是示範和說明 uncontrolled components 還有 useRef
的使用,但是一般來說,在沒有額外需求的情況下,React 的表單還是會使用 Controlled Components,如此才能知道每一次使用者輸入的內容,做出立即的互動效果(例如,錯誤提示、表單驗證等等)。讀者可以參考以下步驟完成:
- 透過
import
匯入useRef
這個 React Hooks - 在
useRef
中帶入預設值為null
,並取得inputLocationRef
物件 - 透過
ref
這個屬性,讓inputLocationRef
可以參照到該 select 元件 - 在使用者選擇不同的地區時,可以透過
inputLocationRef.current
指稱到 Select 元素 - 透過
inputLocationRef.current.value
即可取得使用者選擇的項目值
從這張圖中,可以看到在程式碼上,使用 Controlled Components 和 Uncontrolled Components 上的差異:
雖然透過 Uncontrolled Components 的作法一樣可以完成後續要做的保存使用者地區資訊的功能,但目前依照 React 的建議,在下一個單元中,我們仍會使用上一個單元完成的 Controlled Components 的做法,因此練習完後,記得要修改回原本的程式碼。
#
useRef 的補充說明:在 Functional Component 中建立不會導致畫面更新的變數useRef
除了可以用來參照回某個表單元件外,在 React 中也很常用來建立一個不會使得畫面更新的變數來使用,因此開發者可以把一些變數的資料保存在 useRef
取得物件的 current
屬性中,並在需要時進行修改即可。關於這部分更多的補充,可以檢視本單元於 Github 專案分支的說明頁檢視。
本單元相關之網頁連結、完整程式碼與程式碼變更部分可於 uncontrolled-components-with-use-ref
分支檢視:https://github.com/pjchender/learn-react-from-hook-realtime-weather-app/tree/uncontrolled-components-with-use-ref