目錄:html
你們都知道React中的ref
屬性能夠幫助咱們獲取子組件的實例或者Dom對象,進而對子組件進行修改,是一個很方便的特性。在傳統類組件中,咱們經過使用 React.createRef()
建立的,並經過 ref
屬性附加到 React 元素
來使用。而隨着hooks的愈來愈普遍的使用,咱們有必要了解一下在函數式組件中,如何使用Ref.
想要在函數式組件中使用Ref,咱們必須先了解兩個Api,useRef
和forwardRef
node
const refContainer = useRef(initialValue);
useRef返回一個可變的ref對象,其.current
屬性被初始化爲傳入的參數(initialValue)
。返回的ref對象在整個生命週期內保持不變。
下面看一個例子react
function TextInputWithFocusButton() { // 關鍵代碼 const inputEl = useRef(null); const onButtonClick = () => { // 關鍵代碼,`current` 指向已掛載到 DOM 上的文本輸入元素 inputEl.current.focus(); }; return ( <> // 關鍵代碼 <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
效果以下
api
能夠看到咱們點擊button,先經過useRef
建立一個ref對象inputEl
,而後再將inputEl
賦值給input
的ref
,最後,經過inputEl.current.focus()
就可讓input聚焦。
而後,咱們再想下,若是input不是個普通的dom元素,而是個組件,該怎麼辦呢?
這就牽扯到另一個api,forwardRef
。dom
咱們修改一下上面的例子,將input單獨封裝成一個組件TextInput
。函數
const TextInput = forwardRef((props,ref) => { return <input ref={ref}></input> })
而後用TextInputWithFocusButton
調用它code
function TextInputWithFocusButton() { // 關鍵代碼 const inputEl = useRef(null); const onButtonClick = () => { // 關鍵代碼,`current` 指向已掛載到 DOM 上的文本輸入元素 inputEl.current.focus(); }; return ( <> // 關鍵代碼 <TextInput ref={inputEl}></TextInput> <button onClick={onButtonClick}>Focus the input</button> </> ); }
能夠看到React.forwardRef 接受一個渲染函數,其接收 props 和 ref 參數並返回一個 React 節點。
這樣咱們就將父組件中建立的ref
轉發進子組件,並賦值給子組件的input元素,進而能夠調用它的focus方法。
至此爲止,經過useRef+forwardRef,咱們就能夠在函數式組件中使用ref了。固然,這篇文章還遠不止如此,下面還要介紹兩個重要的知識點useImperativeHandle
和回調Ref
,結合上面兩個api,讓你的代碼更加完美。htm
有時候,咱們可能不想將整個子組件暴露給父組件,而只是暴露出父組件須要的值或者方法,這樣可讓代碼更加明確。而useImperativeHandle
Api就是幫助咱們作這件事的。
語法:對象
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
可讓你在使用 ref 時自定義暴露給父組件的實例值。
一個例子🌰:blog
const TextInput = forwardRef((props,ref) => { const inputRef = useRef(); // 關鍵代碼 useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} /> }) function TextInputWithFocusButton() { // 關鍵代碼 const inputEl = useRef(null); const onButtonClick = () => { // 關鍵代碼,`current` 指向已掛載到 DOM 上的文本輸入元素 inputEl.current.focus(); }; return ( <> // 關鍵代碼 <TextInput ref={inputEl}></TextInput> <button onClick={onButtonClick}>Focus the input</button> </> ); }
這樣,咱們也可使用current.focus()來事input聚焦。這裏要注意的是,子組件TextInput中的useRef對象,只是用來獲取input元素的,你們不要和父組件的useRef混淆了。
什麼是回調Ref
呢?
上面的例子,都有一個問題,就是當 ref
對象內容發生變化時,useRef
並不會通知你。變動 .current
屬性不會引起組件從新渲染,看下面這個例子。
好比下面這個例子:
function TextInputWithFocusButton() { const inputEl = useRef(null); const [value, setValue] = useState(""); useEffect(() => { setValue(inputEl.current.value); }, [inputEl]); const onButtonClick = () => { // `current` 指向已掛載到 DOM 上的文本輸入元素 console.log("input值", inputEl.current.value); setValue(inputEl.current.value); }; return ( <> <div> 子組件: <TextInput ref={inputEl}></TextInput> </div> <div> 父組件: <input type="text" value={value} onChange={() => {}} /> </div> <button onClick={onButtonClick}>得到值</button> </> ); } const TextInput = forwardRef((props, ref) => { const [value, setValue] = useState(""); const inputRef = useRef(); useImperativeHandle(ref, () => ({ value: inputRef.current.value, })); const changeValue = e => { setValue(e.target.value); }; return <input ref={inputRef} value={value} onChange={changeValue}></input>; });
能夠看到,父組件獲取不到子組件實時的值,必須點擊按鈕才能獲取到,即便我寫了useEffect
,但願它在inputEl
改變的時候,從新設置value
的值。
那怎麼辦呢?這就須要回調Ref,咱們改一下代碼
function TextInputWithFocusButton() { const [value, setValue] = useState(""); const inputEl = useCallback(node => { if (node !== null) { console.log("TCL: TextInputWithFocusButton -> node.value", node.value) setValue(node.value); } }, []); const onButtonClick = () => { // `current` 指向已掛載到 DOM 上的文本輸入元素 console.log("input值", inputEl.current.value); setValue(inputEl.current.value); }; return ( <> <div> 子組件: <TextInput ref={inputEl}></TextInput> </div> <div> 父組件: <input type="text" value={value} onChange={() => {}} /> </div> </> ); } const TextInput = forwardRef((props,ref) => { const [value, setValue] = useState('') const inputRef = useRef(); useImperativeHandle(ref, () => ({ value: inputRef.current.value })); const changeValue = (e) =>{ setValue(e.target.value); } return <input ref={inputRef} value={value} onChange={changeValue}></input> })
能夠看到,這裏咱們輸入時,父組件就能夠實時地拿到值了。
這裏比較關鍵的代碼就是使用useCallback
代替了useRef
,callback ref
會將當前ref的值變化通知咱們。
好,以上就是整理的關於函數式組件中使用Ref的方法。
固然,其中關於api介紹的比較簡陋,你們看完可能不知所云。由於此篇文章的主要目的,僅是將散落在官網中關於ref的相關方法進行一下整合,造成一個使用ref的思路,關於api的更深刻的瞭解,還請移步React官網。
若有不足,歡迎指出。
參考文獻:
官網中useRef useImperativeHandle Api的介紹
官網中forwardRef的介紹
官網中回調Ref的介紹