有時候我們只需要一個單純的變數,希望隨著每次 render 都還能保持同樣的 ref,一般的變數又會不停的更新數值,這時候 useRef 就會是你的好幫手啦!
const [state, setState] = React.useState(true);
const handleToggleState = () => setState(!state);
//略
<button onClick={handleToggleState}>TRIGGER</button>
<CardWithGlobalVar onClick={handleToggleState} />
<CardWithGlobalVar onClick={handleToggleState} />
<CardWithFunctionVar />
<CardWithRef />
假設我們有四張卡片,每張卡片都需要一個獨立且固定的號碼,分別都有一個隨機產生的數字:
CardWithGlobalVar
中的數字是從 function 外面宣告變數並賦值CardWithFunctionVar
中的數字是從 function 內宣告變數並賦值CardWithRef
中的數字是透過 useRef 儲存CardWithAction
中有 useRef 儲存可替換數字的狀態,並用useState處理數字而有一顆 button 供我們觸發 state change and re-render。
這邊刻意使用兩個相同元件方便觀察,另外有一個 event 可以觸發 (很dirty的event,純 demo 使用)
透過觸發 event 之後,可以發現到兩個 component 的內容同時變更了,這是我們預期之內的事情。
若今天製作出來的 component 需要一個獨立的value (且不會受其他component而影響),這樣就不能達到我們的目的了。
很常見的一般元件,雖然使用了一個變數儲存,按下按鈕之後,觸發了 render,導致數字又重新生成,這樣的行為也違背了我們想要固定數字的需求。
當我們給 useRef 一個value時,會回傳一個 object,且 value 會存放在 current
const ref = useRef("Hello")
//ref 是一個 object 並持有一個名為 current 的 key
{
current: 'Hello'
}
我們引入 useRef
並給於一個 defaultValue,接著按下按鈕來觸發 render,可以發現我們的數字仍保持不變,useRef
很好的幫我們在元件之間的更新保存了數字,且這個數字是跟隨著每個元件獨立產生的,也就是說再多的卡片都會有一個互不干涉的且獨立的數字了。
假設這張卡片擁有一次替換數字的機會,我們可以改用 useRef
來儲存「是否允許替換」的狀態,數字則用 useState
來處理並呈現數字。
透過點擊替換按鈕之後,我們更新也成功更新了數字,但按下第二次時,就會被擋下來了!
這樣的用法可以應用在較龐大或複雜的 component,減少一些 useState 以及不必要的 update and render。
補充一下,雖然複數的 setState 只會 render 一次(且在 React 18 中更加完善了),不過主要是想傳達在複雜功能中或許不只是單單就按個按鈕就會結束事件,useRef 也可以提供不同面相的狀態保存。
useRef 也可以用來存取 DOM:
fucntion App () {
const elementRef = useRef()
console.log(elementRef.current) //undefined
useEffect(() => {
console.log(elementRef.current) //DOM element
},[])
return <div ref={elementRef}>Hello!</div>
}
這樣我們就可以存取 <div/>
的 DOM 元素進一步操作了,雖然說可以操作,但極大部分情況我們都是用來 READ,而不是嘗試 MUTATE 甚至乾坤大娜移的搬家。
另外也有 callback ref 以及 dynamic 等方式可以存取,但此篇先暫時不做研究。
上述都是提到一般變數的情境,那與 useState 不同在哪呢?
當 useRef 被改變時,React 是不會被通知的,這也就是為什麼 DEMO 中要刻意放一組 useState 來觀察 (雖然做法上較不正確)
請記住 useRef 在其內容有變化時並不會通知你。變更 .current 屬性不會觸發重新 render。
React 官方
後續也會將 useRef 結合到幾個 custom hook,篇幅就先再此打住。