
在這篇文章中,你將學習如何使用
我們將從下面幾點講解:
1 2 3 4 5 6 7 8 9 | 1. 可變值 - 1.1用例:紀錄按鈕點選 - 1.2用例:實現秒錶 2. 訪問DOM元素 - 2.1用例:聚焦輸入 3.更新引用限制 4. 總結 |
可變值
1 2 3 | const reference = useRef(initialValue); reference.current; // 當前的引用 |
1 2 3 4 5 6 7 8 9 10 11 12 | import {<!-- --> useRef } from 'react'; function MyComponent() {<!-- --> const reference = useRef(initialValue); const someHandler = () => {<!-- --> // 訪問引用 const value = reference.current; // 更新引用值 reference.current = newValue; }; } |
關於
- 在套件重新渲染之間,引用的值是永續化的(保持不變);
- 更新引用不會觸發套件重新呈現。
現在,讓我們看看如何在實踐中使用
例項:紀錄按鈕點選
套件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import {<!-- --> useRef } from 'react'; function LogButtonClicks() {<!-- --> const countRef = useRef(0); const handle = () => {<!-- --> countRef.current++; console.log(`Clicked ${<!-- -->countRef.current} times`); }; console.log('I rendered!'); return <button onClick={<!-- -->handle}>Click me</button>; } |
當按鈕被單擊時,handle函式被呼叫,並且引用值被遞增:
注意,更新引用值
'I rendered!'在初始渲染時只會輸出一次。
現在有一個合理的問題:引用和狀態之間的主要區別是什麼?
現在有一個合理的問題:
reference 和 state 之間的主要區別
讓我們重用上一節中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import {<!-- --> useState } from 'react'; function LogButtonClicks() {<!-- --> const [count, setCount] = useState(0); const handle = () => {<!-- --> const updatedCount = count + 1; console.log(`Clicked ${<!-- -->updatedCount} times`); setCount(updatedCount); }; console.log('I rendered!'); return <button onClick={<!-- -->handle}>Click me</button>; } |
每次點選,你會在主控臺中看到「I rendering !」』——這意味著每次狀態更新時,套件都會重新呈現。
所以,state和references之間的兩個主要區別是:
- 更新 state 會觸發套件重新呈現,而更新 ref 則不會。
- state 更新是非同步的(state變數在重新呈現後更新),而ref則同步更新(更新後的值立即可用)
從更高的角度來看,ref 用於儲存套件的基礎設施資料,而 state 儲存直接呈現在螢幕上的訊息。
例項:實現秒錶
你可以儲存在 ref 中的東西是涉及到一些副作用的基礎設施訊息。例如,你可以在ref中儲存不同型態的指標:定時器id,套接字id,等等。
例如,下面的秒錶套件使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import {<!-- --> useRef, useState, useEffect } from 'react'; function Stopwatch() {<!-- --> const timerIdRef = useRef(0); const [count, setCount] = useState(0); const startHandler = () => {<!-- --> if (timerIdRef.current) {<!-- --> return; } timerIdRef.current = setInterval(() => setCount(c => c+1), 1000); }; const stopHandler = () => {<!-- --> clearInterval(timerIdRef.current); timerIdRef.current = 0; }; useEffect(() => {<!-- --> return () => clearInterval(timerIdRef.current); }, []); return ( <div> <div>Timer: {<!-- -->count}s</div> <div> <button onClick={<!-- -->startHandler}>Start</button> <button onClick={<!-- -->stopHandler}>Stop</button> </div> </div> ); } |
要停止秒錶,請單擊「停止」按鈕。停止按鈕處理程式
此外,如果套件在秒錶處於活動狀態時解除安裝,
在秒錶範例中,ref用於儲存基礎架構資料—活動計時器id。
訪問 DOM 元素
-
定義訪問元素
const elementRef = useRef() 的引用; -
將引用賦給元素的
ref 屬性:;
-
引用完成後,
elementRef.current 包含DOM元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import {<!-- --> useRef, useEffect } from 'react'; function AccessingElement() {<!-- --> const elementRef = useRef(); useEffect(() => {<!-- --> const divElement = elementRef.current; }, []); return ( <div ref={<!-- -->elementRef}> I'm an element </div> ); } |
例項:聚焦輸入框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import {<!-- --> useRef, useEffect } from 'react'; function InputFocus() {<!-- --> const inputRef = useRef(); useEffect(() => {<!-- --> inputRef.current.focus(); }, []); return ( <input ref={<!-- -->inputRef} type="text" /> ); } |
然後將
然後,設定
在初始化渲染時 Ref 是 null
在初始渲染時,儲存DOM元素的 ref 是空的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import {<!-- --> useRef, useEffect } from 'react'; function InputFocus() {<!-- --> const inputRef = useRef(); useEffect(() => {<!-- --> // 輸出 `HTMLInputElement` console.log(inputRef.current); inputRef.current.focus(); }, []); // 初始化渲染時輸出 `undefined` console.log(inputRef.current); return <input ref={<!-- -->inputRef} type="text" />; } |
在初始渲染期間,React仍然決定套件的輸出,因此還沒有建立DOM結構。這就是為什麼
當輸入元素在DOM中建立完成後,
更新 references 限制
功能套件的功能範圍應該計算輸出或呼叫鉤子。
這就是為什麼更新 ref (以及更新 state)不應該在套件函式的直接作用域內執行。
ref必須在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import {<!-- --> useRef, useEffect } from 'react'; function MyComponent({<!-- --> prop }) {<!-- --> const myRef = useRef(0); useEffect(() => {<!-- --> myRef.current++; // Good! setTimeout(() => {<!-- --> myRef.current++; // Good! }, 1000); }, []); const handler = () => {<!-- --> myRef.current++; // Good! }; myRef.current++; // Bad! if (prop) {<!-- --> myRef.current++; // Bad! } return <button onClick={<!-- -->handler}>My button</button>; } |
總結
使用初始值呼叫
在套件重新呈現之間,引用的值是持久的。
更新引用與更新狀態相反,不會觸發套件重新呈現。
引用也可以訪問DOM元素。
- 元素在