useRef 說明
useRef
是一個 Hook 可以讓你參考一個值並不會受到 React 的重新渲染影響
以上是根據 React 官方文檔 對 useRef
的說明,但這句話這究竟代表什麼意思?簡單來說我會把 useRef
當作是 useState
但不會觸發重新渲染 ,React 會幫你記錄這筆資料,就算元件重新渲染了還是存在。
範例一:記錄元件的資料狀態,不受渲染影響
這麼說還是有些抽象,讓我們來模擬一個簡單的需求並嘗試實現它:「計算一個元件究竟被重新渲染了幾次」。首先在畫面上定義一個輸入框,當使用者輸入名字時,會在畫面上顯示「我的名字是 x 」以及「我重新渲染了 x 次」。
並且定義資料的部分,這裡使用 useState
來定義 name
和 renderCount
這兩個變數,並且在每次元件刷新時就讓 renderCount
+ 1。
看起來很簡單,應該不會有什麼問題?但實際上前面的程式邏輯上有非常大的漏洞,它無法運作且會瘋狂跳出以下的錯誤!
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
這是因為輸入導致觸發元件重新渲染,這時候觸發 renderCount + 1
又會導致元件重新渲染,進入無限的更新 State > 觸發渲染 > 更新 State > 觸發渲染 …… 😭,這時候就是使用 useRef
的典型時機。
只需要把 renderCount
改成使用 useRef
來定義,程式就可以如期運作了,這是因為 useRef
並不會造成元件重新渲染:
本元件重新渲染次數:1
在上面的例子中示範了如果需要在元件重新渲染時保留某些狀態,就可以使用 useRef
這個 Hook 來記錄。
範例二:選取元素
由於 React 是建構虛擬 DOM 來處理元件的渲染,所以我們無法直接使用 querySelector
,這個方法只能取得真實的 DOM,而不是 React 的虛擬 DOM。在 React 中,我們可以使用 ref
來選取虛擬的 DOM 元素,使用 useRef
記錄下來。
在這個案例中,你可以想像就像是 Vitural DOM 版的 querySelector
,只是這個方法可以在 React 的元件中使用。
總結
既然說到 useRef
就不能不談 useState
與其他定義變數的方式,它們有不同的用途和特點,讓我們來看看它們的差異:
方法一:定義變數在元件外
這麼做就相當於定義了一個全域變數,它會在加載整個應用程式的時候被建立,並且在整個應用程式中都可以使用,這種方式在某些情況下是很方便的,比如當我們有多個元件需要共享同一個變數時,但也會有一些潛在的問題像是:
- 命名衝突:如果多個元件都使用了同一個變數名稱,就會造成命名衝突。
- 難以追蹤:當變數被多個元件共享時,很難追蹤變數是如何被修改的。這可能會導致難以調試的問題。
- 難以測試:導致程式的複雜性和不穩定性上升且難以預估與測試。
綜合下來不會是一個很好的方法,如果希望狀態能夠在元件之間共享,可以傳遞 Props 或使用 useContext 來達成,而非使用全域變數。
方法二:定義變數在元件內
這麼做與方法一類似,只是變數被定義在元件內部,這樣就不會有命名衝突的問題,但一個很大的區別是,這個變數會在每次元件刷新時重置。
方法三:使用 useState
相信學習 React 第一個碰的 Hook 就是 useState
,因此也不贅述,就是當 setState 時,元件會重新渲染。
方法四:使用 useRef
與 useState
類似,但不會觸發重新渲染。
資料來源
- Learn useRef in 11 Minutes - Web Dev Simplified
- useRef - React Docs
- Why need useRef and not mutable variable? - stack overflow