useRef
là một React Hook cho phép bạn tham chiếu một giá trị không cần thiết cho việc hiển thị.
const ref = useRef(initialValue)
Tham khảo
useRef(initialValue)
Gọi useRef
ở cấp cao nhất của component để khai báo một ref.
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
Tham số
initialValue
: Giá trị bạn muốn thuộc tínhcurrent
của đối tượng ref được khởi tạo ban đầu. Nó có thể là một giá trị của bất kỳ kiểu nào. Đối số này bị bỏ qua sau lần hiển thị ban đầu.
Giá trị trả về
useRef
trả về một đối tượng với một thuộc tính duy nhất:
current
: Ban đầu, nó được đặt thànhinitialValue
mà bạn đã truyền. Sau đó, bạn có thể đặt nó thành một giá trị khác. Nếu bạn truyền đối tượng ref cho React dưới dạng thuộc tínhref
cho một nút JSX, React sẽ đặt thuộc tínhcurrent
của nó.
Trong các lần hiển thị tiếp theo, useRef
sẽ trả về cùng một đối tượng.
Lưu ý
- Bạn có thể thay đổi thuộc tính
ref.current
. Không giống như state, nó có thể thay đổi được. Tuy nhiên, nếu nó chứa một đối tượng được sử dụng để hiển thị (ví dụ: một phần của state của bạn), thì bạn không nên thay đổi đối tượng đó. - Khi bạn thay đổi thuộc tính
ref.current
, React không hiển thị lại component của bạn. React không nhận biết được khi bạn thay đổi nó vì ref là một đối tượng JavaScript thuần túy. - Không viết hoặc đọc
ref.current
trong quá trình hiển thị, ngoại trừ khởi tạo. Điều này làm cho hành vi của component của bạn trở nên khó đoán. - Trong Strict Mode, React sẽ gọi hàm component của bạn hai lần để giúp bạn tìm ra những tạp chất vô tình. Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến production. Mỗi đối tượng ref sẽ được tạo hai lần, nhưng một trong các phiên bản sẽ bị loại bỏ. Nếu hàm component của bạn là thuần túy (như nó phải vậy), điều này sẽ không ảnh hưởng đến hành vi.
Cách sử dụng
Tham chiếu một giá trị với ref
Gọi useRef
ở cấp cao nhất của component để khai báo một hoặc nhiều refs.
import { useRef } from 'react';
function Stopwatch() {
const intervalRef = useRef(0);
// ...
useRef
trả về một đối tượng ref với một thuộc tính current
duy nhất ban đầu được đặt thành giá trị ban đầu mà bạn đã cung cấp.
Trong các lần hiển thị tiếp theo, useRef
sẽ trả về cùng một đối tượng. Bạn có thể thay đổi thuộc tính current
của nó để lưu trữ thông tin và đọc nó sau này. Điều này có thể khiến bạn nhớ đến state, nhưng có một sự khác biệt quan trọng.
Thay đổi ref không kích hoạt hiển thị lại. Điều này có nghĩa là ref là hoàn hảo để lưu trữ thông tin không ảnh hưởng đến đầu ra trực quan của component của bạn. Ví dụ: nếu bạn cần lưu trữ một ID khoảng thời gian và truy xuất nó sau này, bạn có thể đặt nó trong một ref. Để cập nhật giá trị bên trong ref, bạn cần thay đổi thủ công thuộc tính current
của nó:
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
Sau đó, bạn có thể đọc ID khoảng thời gian đó từ ref để bạn có thể gọi xóa khoảng thời gian đó:
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
Bằng cách sử dụng ref, bạn đảm bảo rằng:
- Bạn có thể lưu trữ thông tin giữa các lần hiển thị lại (không giống như các biến thông thường, được đặt lại trên mỗi lần hiển thị).
- Thay đổi nó không kích hoạt hiển thị lại (không giống như các biến state, kích hoạt hiển thị lại).
- Thông tin là cục bộ cho mỗi bản sao của component của bạn (không giống như các biến bên ngoài, được chia sẻ).
Thay đổi ref không kích hoạt hiển thị lại, vì vậy ref không phù hợp để lưu trữ thông tin bạn muốn hiển thị trên màn hình. Thay vào đó, hãy sử dụng state. Đọc thêm về lựa chọn giữa useRef
và useState
.
Example 1 of 2: Bộ đếm nhấp chuột
Component này sử dụng ref để theo dõi số lần nút được nhấp. Lưu ý rằng bạn có thể sử dụng ref thay vì state ở đây vì số lần nhấp chỉ được đọc và ghi trong một trình xử lý sự kiện.
import { useRef } from 'react'; export default function Counter() { let ref = useRef(0); function handleClick() { ref.current = ref.current + 1; alert('Bạn đã nhấp ' + ref.current + ' lần!'); } return ( <button onClick={handleClick}> Nhấp vào tôi! </button> ); }
Nếu bạn hiển thị {ref.current}
trong JSX, số sẽ không cập nhật khi nhấp. Điều này là do việc đặt ref.current
không kích hoạt hiển thị lại. Thông tin được sử dụng để hiển thị nên là state.
Thao tác DOM với ref
Việc sử dụng ref để thao tác DOM là đặc biệt phổ biến. React có hỗ trợ tích hợp cho việc này.
Đầu tiên, khai báo một đối tượng ref với một giá trị ban đầu là null
:
import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
Sau đó, chuyển đối tượng ref của bạn làm thuộc tính ref
cho JSX của nút DOM bạn muốn thao tác:
// ...
return <input ref={inputRef} />;
Sau khi React tạo nút DOM và đặt nó trên màn hình, React sẽ đặt thuộc tính current
của đối tượng ref của bạn thành nút DOM đó. Bây giờ bạn có thể truy cập nút DOM của <input>
và gọi các phương thức như focus()
:
function handleClick() {
inputRef.current.focus();
}
React sẽ đặt thuộc tính current
trở lại null
khi nút bị xóa khỏi màn hình.
Đọc thêm về thao tác DOM với ref.
Example 1 of 4: Tập trung vào một ô nhập văn bản
Trong ví dụ này, việc nhấp vào nút sẽ tập trung vào ô nhập:
import { useRef } from 'react'; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <input ref={inputRef} /> <button onClick={handleClick}> Tập trung vào ô nhập </button> </> ); }
Tránh tạo lại nội dung ref
React lưu giá trị ref ban đầu một lần và bỏ qua nó trong các lần hiển thị tiếp theo.
function Video() {
const playerRef = useRef(new VideoPlayer());
// ...
Mặc dù kết quả của new VideoPlayer()
chỉ được sử dụng cho lần hiển thị ban đầu, nhưng bạn vẫn đang gọi hàm này trên mỗi lần hiển thị. Điều này có thể gây lãng phí nếu nó đang tạo ra các đối tượng tốn kém.
Để giải quyết vấn đề này, bạn có thể khởi tạo ref như thế này thay thế:
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...
Thông thường, việc viết hoặc đọc ref.current
trong quá trình hiển thị là không được phép. Tuy nhiên, điều này là ổn trong trường hợp này vì kết quả luôn giống nhau và điều kiện chỉ thực thi trong quá trình khởi tạo nên nó hoàn toàn có thể đoán trước được.
Tìm hiểu sâu
Nếu bạn sử dụng trình kiểm tra kiểu và không muốn luôn kiểm tra null
, bạn có thể thử một mẫu như thế này thay thế:
function Video() {
const playerRef = useRef(null);
function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}
// ...
Ở đây, bản thân playerRef
có thể null. Tuy nhiên, bạn sẽ có thể thuyết phục trình kiểm tra kiểu của mình rằng không có trường hợp nào getPlayer()
trả về null
. Sau đó, sử dụng getPlayer()
trong các trình xử lý sự kiện của bạn.
Khắc phục sự cố
Tôi không thể lấy ref cho một component tùy chỉnh
Nếu bạn cố gắng chuyển một ref
cho component của riêng bạn như thế này:
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
Bạn có thể gặp lỗi trong bảng điều khiển:
Theo mặc định, các component của riêng bạn không hiển thị ref cho các nút DOM bên trong chúng.
Để khắc phục điều này, hãy tìm component mà bạn muốn lấy ref:
export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}
Và sau đó thêm ref
vào danh sách các prop mà component của bạn chấp nhận và chuyển ref
làm prop cho component tích hợp sẵn có liên quan như thế này:
function MyInput({ value, onChange, ref }) {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
};
export default MyInput;
Sau đó, component cha có thể lấy ref cho nó.
Đọc thêm về truy cập các nút DOM của component khác.