Sai lầm lớn nhất của Dev khi chuyển từ Class sang Hooks là cố gắng map useEffect vào componentDidMount hay componentWillUnmount.
Hãy thay đổi tư duy: Hooks không phải là vòng đời (Lifecycle). Hooks là công cụ để Đồng bộ hóa (Synchronization) giữa State/Props và các hiệu ứng phụ (Side Effects).
1. Tư duy về useEffect
Thay vì hỏi: "Hàm này chạy khi nào?", hãy hỏi: "Sync cái gì với cái gì?".
- Mounting: Đồng bộ trạng thái ban đầu của hệ thống bên ngoài (API, DOM).
- Updating: Đồng bộ hóa lại mỗi khi có dữ liệu phụ thuộc thay đổi.
- Unmounting: Dọn dẹp đồng bộ để tránh rò rỉ bộ nhớ.
useEffect(() => {
const connection = createConnection(roomId); // Bắt đầu đồng bộ với Socket
connection.connect();
return () => {
connection.disconnect(); // Ngắt đồng bộ khi không cần nữa
};
}, [roomId]); // Chạy lại mỗi khi roomId thay đổi2. Quy tắc Dependency Array chuẩn Senior
Dependency Array không phải là một danh sách các biến bạn "muốn" nó chạy lại. Nó là một lời cam kết với React về những gì effect của bạn sử dụng.
- Mảng Rỗng
[]: "Tôi không phụ thuộc vào bất cứ thứ gì. Tôi chỉ đồng bộ một lần." - Có biến
[id, user]: "Tôi sử dụngidvàuser. Bất cứ khi nào 1 trong 2 cái này đổi, hãy sync lại cho tôi."
Cạm bẫy: Đừng lừa dối React (Lying dependencies). Nếu effect dùng userId nhưng bạn không bỏ vào array -> Bạn đang tạo ra Stale Closures (Dữ liệu cũ bị kẹt trong hàm).
3. useState và Cơ chế Batching
React không cập nhật state ngay lập tức sau khi gọi setCount. Nó gom nhóm (Batching) các lần thay đổi để render một lần duy nhất.
const [count, setCount] = useState(0);
const handleAdd = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Kết quả: count chỉ tăng lên 1!
};
// Giải pháp: Dùng functional update nếu state mới dựa trên state cũ
const handleAddCorrect = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Kết quả: count tăng lên 3
};4. useMemo & useCallback: Khi nào nên dùng?
Đừng lạm dụng! Tối ưu hóa quá sớm là "nguồn gốc của mọi tội lỗi".
- Dùng
useMemo: Khi có một phép tính toán cực nặng (loop 10.000 phần tử) mà kết quả không đổi nếu input không đổi. - Dùng
useCallback: Khi truyền một function xuống cho một Component con đã được bọcReact.memo. Nếu không, Component con sẽ luôn re-render vì tham chiếu function thay đổi.
5. Luật chơi của Hooks (The Rules)
- Chỉ gọi ở tầng Top-level: Không gọi trong
if,for, hay function con. React cần thứ tự gọi hooks luôn cố định để quản lý state. - Chỉ gọi trong React Functions: Component hoặc Custom Hook.
Kết luận
Hooks mang lại sức mạnh trừu tượng hóa logic cực cao qua Custom Hooks. Hãy tập trung vào việc hiểu cơ chế "Closure" và "Synchronization" thay vì cố gắng nhớ các lifecycles cũ.