react hooks系列之useRef


react hooks是 react 16.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元素。在一個組件的正常的生命周期中可以大致可以分為3個階段

  1. 從創建組件到掛載到DOM階段。初始化props以及state, 根據state與props來構建DOM
  2. 組件依賴的props以及state狀態發生變更,觸發更新
  3. 銷毀階段

在第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注冊到INPUTTEXTAREA、還是其他的第三方組件。注冊一個還是多個。使用回調refs我們能夠直接獲取到對應的真實DOM元素或者React實例,而使用了refs就會失去這種靈活性。

如果我們繼續往后面進行分析,會看到useForm這個hook中,使用了大量的useRef來存儲變量,原因看前面。

而前面通過register中調用的ref對象被注冊到filedsRef中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM