React cung cấp cho bạn hai cách để duy trì dữ liệu bên trong một component: useStateuseRef. Thoạt nhìn chúng có vẻ giống nhau. Cả hai đều trả về một thứ mà bạn có thể đọc được, cả hai đều tồn tại qua các lần re-render, và cả hai đều cho phép bạn ghi nhớ một giá trị từ lần click này sang lần click khác. Tuy nhiên, nếu chọn sai, bạn sẽ kết thúc với một màn hình từ chối cập nhật hoặc một chuỗi re-render không cần thiết kéo dài vô tận. Sự lựa chọn này không nằm ở cú pháp. Nó nằm ở việc liệu React có cần phải biết hay không.

Bản chất của từng Hook

useState là kênh giao tiếp chính thức của React với một component. Khi bạn gọi nó, bạn nhận lại một giá trị và một hàm setter. React theo dõi giá trị đó như một phần định danh của component. Khi hàm setter được kích hoạt, React sẽ nói: "Có gì đó đã thay đổi," và lên lịch một lần render mới để màn hình có thể cập nhật theo.

Ngược lại, useRef không gì khác hơn là một đối tượng JavaScript thuần túy với thuộc tính current. React hứa sẽ đưa cho bạn chính xác cùng một tham chiếu đối tượng đó trong mỗi lần render. Nó không theo dõi những gì nằm bên trong. Việc thay đổi (mutating) someRef.current diễn ra một cách âm thầm. React sẽ không phản ứng gì cả.

Sự im lặng đó chính là mục đích cốt lõi. Refs là một "lối thoát" (escape hatch), không phải là sự thay thế cho state.

Sự phân chia về Rendering

Nếu bạn thay đổi state, component sẽ re-render. Đây là hành vi mà hầu hết những người mới bắt đầu mong đợi, và đó chính xác là những gì bạn muốn khi dữ liệu mới cần phải hiển thị trên màn hình. Một bộ đếm, một trường nhập liệu (form field), một danh sách người dùng được lấy về—nếu người dùng nhìn thấy nó, nó có lẽ thuộc về state. Toàn bộ luồng dữ liệu của React được xây dựng dựa trên ý tưởng rằng các thay đổi về state sẽ thông báo cho bộ renderer để đồng bộ hóa DOM.

Nếu bạn thay đổi một ref, sẽ không có gì thay đổi về mặt hình ảnh. Biến sẽ được cập nhật ngay lập tức và đồng bộ, nhưng component không re-render. Điều này làm cho refs trở nên lý tưởng cho các giá trị hỗ trợ các hoạt động bên trong của component mà không cần là một phần của kết quả hiển thị trực quan. Hãy nghĩ về các ID của timer, các bản chụp (snapshots) của props trước đó, hoặc các handle DOM trực tiếp. UI không quan tâm đến ID của interval điều khiển tính năng tự động chạy của carousel; nó chỉ quan tâm đến việc slide nào đang hiển thị. ID của interval nên nằm trong một ref.

Khi nào State là công cụ phù hợp

Hãy sử dụng useState bất cứ khi nào một giá trị là một phần của bề mặt UI của bạn.

Các trường nhập liệu (input fields) là một ví dụ hiển nhiên. Nếu người dùng nhập một địa chỉ email và bạn cần xác thực nó cũng như hiển thị thông báo lỗi bên dưới ô nhập, thì chuỗi email đó cần phải là state. Logic xác thực và biểu ngữ lỗi đều phụ thuộc vào giá trị mới nhất, và React chỉ biết cập nhật biểu ngữ vì state đã kích hoạt một lần re-render.

Các bộ đếm và các nút chuyển đổi (toggles) là một ví dụ điển hình khác. Một nút tăng điểm, một cờ (flag) đóng/mở modal, một chỉ số tab—tất cả đều chảy qua state vì kết quả render thay đổi cùng với giá trị. Ngay cả các giá trị phái sinh (derived values), chẳng hạn như một danh sách đã được lọc phụ thuộc vào một chuỗi tìm kiếm, thường cũng bắt đầu với state vì giá trị nguồn hiển thị cho người dùng.

Cũng có một sắc thái về thời gian cần được hiểu rõ. Các cập nhật state là bất đồng bộ và được gom nhóm (batched). Nếu bạn gọi setCount(count + 1) ba lần bên trong một trình xử lý sự kiện, React không render ba lần. Nó gom chúng lại thành một lần cập nhật duy nhất. Biến count bên trong hàm đang chạy của bạn cũng sẽ giữ giá trị cũ (stale) cho đến lần render tiếp theo. Việc gom nhóm này là một tính năng. Nó giúp ứng dụng chạy nhanh. Nhưng điều đó có nghĩa là bạn không thể mong đợi biến state phản ánh giá trị mới ngay ở dòng lệnh tiếp theo.

Khi nào Refs "cứu cánh" cho bạn

Sử dụng useRef cho các phần hạ tầng, chứ không