useRef tạo một mutable object có property .current sống xuyên suốt vòng đời component, và thay đổi .current không trigger re-render.
Ví von: useRef giống một chiếc hộp nhỏ bạn gắn vào component. Bạn mở hộp ra, bỏ gì vào cũng được, lấy ra bất cứ lúc — nhưng hành động đó không khiến React vẽ lại UI.
Cơ chế bên trong: Mỗi component có một mảng ref internally — React giữ nguyên cùng một object { current: ... } qua mọi render. Khi bạn gán ref cho JSX element, React sẽ tự cập nhật .current thành DOM node tương ứng mỗi khi render xong (hoặc null khi unmount). Đây là lý do ref có thể truy cập DOM mà không cần state.
Hai công dụng chính:
1. Truy cập DOM element — focus, measure, hoặc thao tác trực tiếp với node.
2. Lưu giá trị mutable giữa các render — giữ timer ID, subscription, giá trị trước đó, hoặc bất kỳ thứ gì cần "ghi nhớ" mà không cần React vẽ lại.
const FormExample = () => {
// (1) DOM ref: focus input khi mount
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => { inputRef.current?.focus() }, [])
// (2) mutable value: lưu timerId mà không trigger re-render
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
const start = () => { timerRef.current = setInterval(tick, 1000) }
const stop = () => { if (timerRef.current) clearInterval(timerRef.current) }
return <input ref={inputRef} placeholder="auto-focused" />
}Edge case / lỗi thường gặp:
- useRef không tự render lại component. Nếu bạn muốn UI cập nhật theo giá trị đó, hãy dùng useState.
- Giá trị trong .current chỉ giữ nguyên reference; nếu bạn lưu object mutable, thay đổi trực tiếp property của nó sẽ không trigger re-render — đôi khi gây ra bug khó debug.
- Gán ref callback (ref={node => ...}) thay vì ref={inputRef} khi cần chạy code mỗi lần ref thay đổi.
Lưu ý: Nếu mục tiêu là "giữ giá trị mà không re-render" nhưng bạn cần React biết khi giá trị thay đổi để điều phối effect, useRef không thay thế được useState. Đúng cách là useState cho giá trị liên quan UI, useRef cho giá trị "ẩn" bên trong.