react hooks系列之useRef
reacthooks是react16.8 引入的特性,這裡我們通過對react-hook-form進行分析來了解成熟的庫是如何使用hook的。這將是一個系列,首先推薦 useRef
簡介
在react中,我們使用Ref來獲取元件的例項或者DOM元素。我們可以使用兩種方式來建立Ref
import * as React from 'react'
import { useState, useEffect, useRef, createRef } from 'react'
export default () => {
const ref1 = createRef<htmlFormElement>()
const ref2 = useRef<htmlInputElement>()
useEffect(() => {
console.log(ref1)
console.log(ref2)
}, [])
return (
<form ref={ref1}>
<label>使用者資訊</label>
<input type="text" ref={ref2} />
</form>
)
}
上面兩種方式都能在元件mounted
- 從建立元件到掛載到DOM階段。初始化props以及state, 根據state與props來構建DOM
- 元件依賴的props以及state狀態發生變更,觸發更新
- 銷燬階段
在第1階段,使用createRef與useRef兩者是沒有區別。但是在第2階段, 也就是更新階段兩者是有區別的。我們知道,在一個區域性函式中,函式每一次執行,都會在把函式的變數重新生成一次。
export default () => {
const ref1 = createRef<HTMLFormElement>()
const ref2 = useRef<HTMLInputElement>()
const [ count, setCount ] = useState<number>(0)
useEffect(() => {
if (!store.ref1) {
store.ref1 = ref1
} else {
console.log(store.ref1 === ref1)
}
})
useEffect(() => {
setTimeout(() => {
setCount(1)
}, 1000)
}, [])
return (
<form ref={ref1}>
<label>使用者資訊</label>
<input type="text" ref={ref2} />
</form>
)
}
我們使用一個外部的變數store來儲存初次所建立的ref,在我們對元件進行更新後,會發現更新後的ref與我們初次建立的ref其實並不一致。這樣也就意味著我們每更新一次元件, 就重新建立一次ref。
由於有上面的問題,這在函式元件中,使用createRef去獲取ref是不合理的。所以hook給我們提供一個新的API, 就是useRef。在useRef建立的ref彷彿就像外部定義的一個全域性變數,不會隨著元件的更新而重新建立。但元件銷燬,它也會消失,不用手動進行銷燬。
export default () => {
const ref1 = createRef<HTMLFormElement>()
const ref2 = useRef<HTMLInputElement>()
const [ count, setCount ] = useState<number>(0)
useEffect(() => {
if (!store.ref1) {
store.ref1 = ref1
} else {
console.log('ref1:', store.ref1 === ref1)
}
if (!store.ref2) {
store.ref2 = ref2
} else {
console.log('ref2:', store.ref2 === ref2)
}
})
useEffect(() => {
setTimeout(() => {
setCount(1)
}, 1000)
}, [])
return (
<form ref={ref1}>
<label>使用者資訊</label>
<input type="text" ref={ref2} />
</form>
)
}
通過上面的說明,我們知道useRef建立的ref並不會隨著元件的更新而重新構建。由於這個特性,在使用react-hook的時候,可以使用useRef來儲存常量。
資源搜尋網站大全 http://www.szhdn.com 廣州VI設計公司https://www.houdianzi.com
useRef在react-hook-form中應用
現在回到我們的主題,看看react-hook-form是如何處理ref。在react-hook-form有一個API為register。
原始碼實現如下
...
function register(refOrValidationOptions, validationOptions) {
if (isWindowUndefined) {
return;
}
if (isString(refOrValidationOptions)) {
registerFieldsRef({ name: refOrValidationOptions }, validationOptions);
return;
}
if (isObject(refOrValidationOptions) && 'name' in refOrValidationOptions) {
registerFieldsRef(refOrValidationOptions, validationOptions);
return;
}
return (ref) => ref && registerFieldsRef(ref, refOrValidationOptions);
}
使用閉包儲存了對當前輸入框的validationOptions, 返回的函式被ref的接收。這裡使用了ref另外一種獲取方式‘回撥refs’
import { useForm } from 'react-hook-form'
export default () => {
const { register, errors, handleSubmit } = useForm()
const submit = useCallback((data, e) => {
console.log(data, e)
}, [])
useEffect(() => {
console.log(errors)
})
return (
<form onSubmit={handleSubmit(submit)}>
<label>使用者資訊</label>
<input name="userName" ref={register({ required: true })} />
{errors.userName && "Your input is required"}
<button type={'submit'}>提交</button>
</form>
)
}
這裡為什麼使用回撥refs,而不是refs。其實理由很簡單,因為後面要引用的DOM元素或者React例項是未知,我們是不知道使用者會把register註冊到INPUT、TEXTAREA、還是其他的第三方元件。註冊一個還是多個。使用回撥refs我們能夠直接獲取到對應的真實DOM元素或者React例項,而使用了refs就會失去這種靈活性。
如果我們繼續往後面進行分析,會看到useForm這個hook中,使用了大量的useRef來儲存變數,原因看前面。
而前面通過register中呼叫的ref物件被註冊到filedsRef中。