📋 Mục lục
useState– Cơ sở của stateuseEffect– Tác dụng phụ và vòng đờiuseContext– Chia sẻ state mà không cần propsuseReducer– Logic state phức tạp và có thể dự đoánuseRef– Tham chiếu, lưu trữ và thoátuseMemovàuseCallback– Tối ưu hóa hiệu suất- Custom Hooks – Tạo logic tái sử dụng
- Lỗi thường gặp và giải pháp
useRef là gì và tại sao cần sử dụng?
useRef là một hook linh hoạt với hai trường hợp sử dụng chính, mặc dù có vẻ khác nhau, nhưng đều dựa trên cùng một nguyên tắc: cung cấp một đối tượng có thể thay đổi (.current) mà tồn tại suốt vòng đời của component mà không gây ra re-render khi giá trị thay đổi.
- Truy cập vào các phần tử DOM: Đây là "lối thoát" để tương tác trực tiếp với một phần tử DOM, chẳng hạn như để quản lý tiêu điểm, đo kích thước hoặc tích hợp các thư viện bên thứ ba (như D3.js).
- Lưu trữ "biến phiên bản": Giúp bạn giữ một giá trị có thể thay đổi mà không cần kích hoạt một render mới. Nó giống như việc có một biến phiên bản trong một component class. Rất thích hợp để lưu ID của các timer, bộ đếm nội bộ, hoặc bất kỳ giá trị nào bạn cần tồn tại giữa các lần render.
Cú pháp và các đặc điểm chính
javascript
const miRef = useRef(valorInicial);
useReftrả về một đối tượng với một thuộc tính duy nhất:current.miRef.currentđược khởi tạo vớivalorInicial.- Bạn có thể thay đổi
miRef.currenttrực tiếp:miRef.current = nuevoValor. - Quan trọng: Thay đổi
miRef.currentKHÔNG kích hoạt một re-render.
Ví dụ thực tiễn chi tiết: input với tiêu điểm và bộ đếm render
Ví dụ này cho thấy cả hai cách sử dụng của useRef: một cho DOM và một cho giá trị bền vững.
javascript
import React, { useState, useEffect, useRef } from 'react';
function FocusCounter() {
const [inputValue, setInputValue] = useState('');
// Trường hợp sử dụng 1: Tham chiếu đến DOM
const inputRef = useRef(null);
// Trường hợp sử dụng 2: Biến bền vững
const renderCount = useRef(0);
useEffect(() => {
// Thực hiện sau mỗi lần render
renderCount.current = renderCount.current + 1;
});
const handleFocus = () => {
// Truy cập vào phần tử DOM trực tiếp
inputRef.current.focus();
};
return (
<div>
<h3>Ví dụ về useRef</h3>
<input
ref={inputRef} // Kết nối ref với phần tử input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Nhập gì đó..."
/>
<button onClick={handleFocus}>Đặt tiêu điểm vào Input</button>
<p>Giá trị của input là: "{inputValue}"</p>
<p>Component này đã được render {renderCount.current} lần.</p>
<p>(Lưu ý: bộ đếm render không gây ra re-renders bởi chính nó)</p>
</div>
);
}
Trong ví dụ này:
inputRefcho phép chúng ta truy cập trực tiếp vào<input>, cho phép chúng ta gọi hàm.focus().renderCounttheo dõi số lần render. Nếu chúng ta sử dụnguseStatecho điều này, mỗi lần cập nhật bộ đếm sẽ gây ra một render khác, tạo ra vòng lặp vô hạn. VớiuseRef, chúng ta có thể cập nhật giá trị mà không có tác dụng phụ.
✅ Thực hành tốt và các mẫu phổ biến
-
Khi nào sử dụng
useRefso vớiuseState:- Sử dụng
useStatenếu sự thay đổi trong giá trị cần phải hiển thị trong UI. - Sử dụng
useRefnếu bạn cần giá trị tồn tại giữa các lần render nhưng không muốn sự thay đổi gây ra một render mới.
- Sử dụng
-
Lưu trữ giá trị trước đó của props hoặc state: Một mẫu rất hữu ích là kết hợp
useRefvàuseEffectđể "nhớ" một giá trị từ render trước đó.javascriptfunction Componente({ valor }) { const valorPrevioRef = useRef(); useEffect(() => { valorPrevioRef.current = valor; }, [valor]); // Cập nhật sau khi render hoàn tất const valorPrevio = valorPrevioRef.current; // ... } -
Quản lý Timers:
useRefrất lý tưởng để lưu ID củasetIntervalhoặcsetTimeoutđể có thể dọn dẹp sau này.
🚨 Lỗi thường gặp và cách tránh
-
Lỗi: Truy cập vào
ref.currentquá sớm.javascript// SAI ❌ function MiComponente() { const miRef = useRef(null); // Ở đây, trong lần render đầu tiên, miRef.current là `null` // vì DOM vẫn chưa tồn tại. Điều này sẽ gây lỗi. miRef.current.focus(); return <input ref={miRef} />; }- Giải pháp: Truy cập vào ref bên trong một
useEffect(được thực hiện sau khi render) hoặc trong các trình xử lý sự kiện (được thực hiện qua tương tác của người dùng).
javascript// ĐÚNG ✅ useEffect(() => { // Đối với lần render đầu tiên, điều này sẽ thực hiện sau khi input tồn tại. miRef.current.focus(); }, []); // Mảng rỗng để chỉ thực hiện một lần - Giải pháp: Truy cập vào ref bên trong một
-
Lỗi: Hiển thị giá trị của một ref trong UI và mong đợi rằng nó sẽ được cập nhật.
- Giải pháp: Nếu bạn cần sự thay đổi được phản ánh trong render, giá trị đó phải nằm trong state (
useState). Nhớ rằng:useRefkhông thông báo cho React khi nó thay đổi.
- Giải pháp: Nếu bạn cần sự thay đổi được phản ánh trong render, giá trị đó phải nằm trong state (
🚀 Thử thách thực hành
- Trình phát Video: Tạo một component hiển thị video (
<video>). Sử dụnguseRefđể lấy tham chiếu đến phần tử và thêm các nút cho "Phát", "Tạm dừng" và "Dừng". - Đồng hồ Bấm Giây Chính Xác: Tạo một đồng hồ bấm giây sử dụng
setInterval. Lưu ID của interval trong một ref để có thể dừng và khởi động lại đúng cách. - Cuộn đến một Phần Tử: Tạo một danh sách dài các phần tử. Thêm các nút ở đầu để khi nhấp vào, cuộn tự động đến một phần tử cụ thể ở giữa danh sách (gợi ý:
element.scrollIntoView()).