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);
// ...

Xem thêm các ví dụ bên dưới.

Tham số

  • initialValue: Giá trị bạn muốn thuộc tính current 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ành initialValue 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ính ref cho một nút JSX, React sẽ đặt thuộc tính current 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 useRefuseState.

Ví dụ về tham chiếu một giá trị với useRef

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.

Chú Ý

Không viết hoặc đọc ref.current trong quá trình hiển thị.

React mong đợi rằng phần thân của component của bạn hoạt động như một hàm thuần túy:

  • Nếu các đầu vào (props, statecontext) giống nhau, nó sẽ trả về chính xác cùng một JSX.
  • Gọi nó theo một thứ tự khác hoặc với các đối số khác nhau sẽ không ảnh hưởng đến kết quả của các lệnh gọi khác.

Đọc hoặc ghi ref trong quá trình hiển thị phá vỡ những kỳ vọng này.

function MyComponent() {
// ...
// 🚩 Không viết ref trong quá trình hiển thị
myRef.current = 123;
// ...
// 🚩 Không đọc ref trong quá trình hiển thị
return <h1>{myOtherRef.current}</h1>;
}

Bạn có thể đọc hoặc ghi ref từ các trình xử lý sự kiện hoặc hiệu ứng thay thế.

function MyComponent() {
// ...
useEffect(() => {
// ✅ Bạn có thể đọc hoặc ghi ref trong các hiệu ứng
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ Bạn có thể đọc hoặc ghi ref trong các trình xử lý sự kiện
doSomething(myOtherRef.current);
}
// ...
}

Nếu bạn phải đọc hoặc viết một cái gì đó trong quá trình hiển thị, hãy sử dụng state thay thế.

Khi bạn phá vỡ các quy tắc này, component của bạn vẫn có thể hoạt động, nhưng hầu hết các tính năng mới hơn mà chúng tôi đang thêm vào React sẽ dựa trên những kỳ vọng này. Đọc thêm về giữ cho component của bạn thuần túy.


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 đầunull:

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.

Ví dụ về thao tác DOM với useRef

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

Làm thế nào để tránh kiểm tra null khi khởi tạo useRef sau

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:

Console
TypeError: Không thể đọc các thuộc tính của null

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.