React 的第二個 hook:useRef

再來一個。

懶人包

以下是幾個很重要的特性:

  1. 回傳值是一個有 current 屬性的 Object
  2. 更新 current 值不會觸發 re-render
  3. re-render 的時後不會被重新賦值

這些是我覺得最重要的幾點,請務必弄清楚。

用來存取 Uncontrolled Component

通常是用在 Uncontrolled Component(沒有 state 的 Component) 上,不過這邊為了方便會直接拿 <input> 來舉例(實務上通常會把 <input> 當成 controlled Component):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useRef } from 'react'

function TodoHeader () {
// 使用這個 hook
const inputRef = useRef()
const handleInput = (e) => {
// 要透過 current 才能存取到(因為 pass-by-reference 的問題)
console.log(inputRef.current.value)
}

return (
<TodoHeaderWrapper onSubmit={handleSubmit}>
<TodoInputBlock>
// 在這裡傳入 ref 這個 props
<Input ref={inputRef} value={value} onChange={handleInput}></Input>
<BlueButton>送出</BlueButton>
</TodoInputBlock>
</TodoHeaderWrapper>
)
}

一開始的 inputRef 應該會是一個空物件 {current: null}(我猜的啦),接著當你把它當作 props 給 Component 的時候就會被變成那個 DOM 元素。

所以你才能在其他 function 中透過 inputRef 來抓到對應的 DOM 元素。

不希望 re-render 後被重新賦值

有時候沒注意好的話可能就會犯這種錯:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App () {
// 宣告一個 id
let id = 1;
// 為了觸發 re-render 而開的 state
const [myState,setMyState] = useState(0)
return (
<div>id: {id}</div>
<div>myState: {myState}</div>
// 點按鈕時就更新 state,並把 id++
<button onClick={() => {
setMyState(myState + 1)
id++
}}>Increment id</button>
)
}

problem

這邊碰到的問題是怎麼 id++ 沒用?

原來是因為每次 re-render 的時候又會執行到 let id = 1 這段,這樣就等於又重新賦值了,所以結果還是會顯示 1,而不是遞增後的值。

要解決這個問題有兩種做法,一種是把 let id = 1 宣告在 Component 的外面,這樣 re-render 時就不會重新宣告一次。

另一種作法是用 useRef 來處理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function App () {
const id = useRef(1); // { current : 1 }
// 為了觸發 re-render 而開的 state
const [myState,setMyState] = useState(0)
return (
<div>id: {id.current}</div>
<div>myState: {myState}</div>
// 點按鈕時就更新 state,並把 id++
<button onClick={() => {
setMyState(myState + 1)
id.current++
}}>Increment id</button>
)
}

solution

雖然 re-render 的時候看起來好像會再執行一次 const id = useRef(1),不過 useRef 的特性就是會把值給保留起來,所以實際上是不會被重新賦值的。

只有像 id.current++ 這種「自己去改變數值」的情況下它的值才會改變。

所以 useRef 就有點像是 useEffect 的顛倒過來。一個不能直接改值,一個可以;而一個在改值後會 re-render, 一個不會。

React 的第三個 hook:useEffect React 的第一個 hook:useState
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×