React hooks là những hàm có tên bắt đầu bằng use, cho phép function component dùng các tính năng của React: useState để ghi nhớ một giá trị, useEffect để chạy code sau khi render, useRef để giữ một DOM node, useContext để đọc dữ liệu dùng chung. Bạn gọi chúng ở đầu component, và React giữ giá trị của chúng giữa các lần render.
Ở đây có bốn hook phối hợp với nhau. useState lưu chữ đã gõ, useRef trỏ đến ô input để nút bấm có thể focus vào nó, useMemo giữ danh sách đã lọc cho đến khi query thay đổi, còn useEffect ghi log sau mỗi lần render mà query thay đổi. Gõ an và theo dõi Console: mỗi lần gõ phím một dòng, xuất hiện sau khi danh sách cập nhật.
Vì sao có hook
Trước React 16.8, một component cần state hoặc code lifecycle phải là một class. Logic của một tính năng bị chia ra componentDidMount, componentDidUpdate và componentWillUnmount, và muốn chia sẻ nó giữa các component thì phải dùng mixin (vốn không bao giờ chạy được với cú pháp class), higher order component hoặc render props, những cách đều bọc cây component của bạn trong thêm nhiều lớp.
// The class way: one feature, three methods
class Clock extends React.Component {
state = { now: new Date() };
componentDidMount() {
this.id = setInterval(() => this.setState({ now: new Date() }), 1000);
}
componentWillUnmount() {
clearInterval(this.id);
}
render() {
return <p>{this.state.now.toLocaleTimeString()}</p>;
}
}
Với hook, cùng tính năng đó nằm ở một chỗ: effect khởi động bộ đếm giờ và hàm cleanup của nó dừng bộ đếm.
Ẩn đồng hồ đi và Console in ra timer stopped: React đã chạy cleanup khi Clock rời khỏi màn hình. Hiện lại và một bộ đếm giờ mới bắt đầu. Class component vẫn chạy trong React 19, nhưng code mới dùng hook.
Mọi hook có sẵn
React 19.2 nhóm các hook theo thứ chúng mang lại cho component. Bạn sẽ dùng hai nhóm đầu hằng ngày, còn những hook khác khi gặp một vấn đề cụ thể cần đến.
| Hook | Mang lại cho bạn | Trang |
|---|---|---|
useState | Một giá trị tồn tại qua các lần render và một hàm set để render lại | useState |
useReducer | State được cập nhật bằng hàm reducer và các action được dispatch | useReducer |
useContext | Một giá trị do component cha cung cấp, không cần truyền props | useContext |
useRef | Một chiếc hộp có thể thay đổi, tồn tại qua các lần render, thường là một DOM node | useRef |
useImperativeHandle | Chọn những gì component cha thấy qua ref của bạn | forwardRef |
useEffect | Code thường chạy sau khi vẽ: timer, subscription, fetch dữ liệu | useEffect |
useLayoutEffect | Code chạy sau khi DOM cập nhật nhưng trước khi vẽ | useLayoutEffect |
useInsertionEffect | Chạy trước layout effect, dành cho các thư viện CSS-in-JS | |
useEffectEvent | Một hàm mà effect có thể gọi, luôn thấy props và state mới nhất | useEffect |
useMemo | Cache một giá trị đã tính giữa các lần render | useMemo |
useCallback | Cache một hàm giữa các lần render | useCallback |
useTransition | Đánh dấu một lần cập nhật là không gấp và cho bạn biết khi nó đang chờ | useTransition |
useDeferredValue | Một bản sao của giá trị, chậm lại phía sau khi render nặng | useDeferredValue |
useId | Một id duy nhất, khớp nhau giữa máy chủ và máy khách | useId |
useSyncExternalStore | Subscribe vào một store nằm ngoài React | useSyncExternalStore |
useDebugValue | Một nhãn cho custom hook trong React DevTools | |
use | Đọc một promise hoặc một context, và có thể nằm trong câu lệnh điều kiện | use |
useActionState | State được một form action cập nhật, kèm cờ đang chờ | useActionState |
useOptimistic | Hiển thị kết quả mong đợi trước khi máy chủ trả lời | useOptimistic |
useFormStatus | Trạng thái chờ của form cha (từ react-dom) |
use, useActionState, useOptimistic và useFormStatus ra mắt cùng React 19, còn useEffectEvent trở nên ổn định trong 19.2. Mọi hook khác trong danh sách cũng chạy được trong React 18.
State gây render, ref thì không
Không phải hook nào cũng kích hoạt render. useState và useReducer thì có: thay đổi giá trị của chúng là React vẽ lại component. useRef đưa cho bạn một đối tượng có trường current mà bạn tùy ý thay đổi, và React không bao giờ render lại vì nó. Vì vậy đó là chỗ thích hợp để giữ id của timer, giá trị trước đó, hay một con số không hiển thị trên màn hình.
useReducer giữ giỏ hàng và useState giữ ghi chú; cả hai đều gây render khi thay đổi, và effect ghi một dòng log cho mỗi lần render. Effect cũng thay đổi commits.current, nhưng thay đổi đó không bao giờ tự gây ra render, nên log không bị lặp vô hạn. Con số được cập nhật trong effect chứ không phải trong thân component, vì thân component không nên thay đổi gì trong lúc render.
Quy tắc của hook
Mọi hook đều tuân theo hai quy tắc:
- Gọi hook ở cấp cao nhất của component, không bao giờ trong câu lệnh điều kiện, vòng lặp hay hàm lồng nhau.
- Chỉ gọi hook từ function component hoặc từ custom hook của chính bạn.
React không nhận biết hook theo tên. Nó nhận biết chúng theo thứ tự bạn gọi, và mong thứ tự đó giống nhau ở mọi lần render. Một hook nằm trong if sẽ làm lệch mọi hook phía sau. Trang quy tắc của hook cho thấy cái gì bị hỏng và cách sửa. use là ngoại lệ duy nhất: nó có thể được gọi bên trong câu lệnh điều kiện.
Custom hook đầu tiên của bạn
Khi hai component cần cùng một logic có state, hãy chuyển logic đó vào một hàm có tên bắt đầu bằng use. Hàm đó có thể gọi bất kỳ hook nào khác. Mỗi component gọi nó sẽ có state riêng.
Cả hai bộ đếm chạy cùng một hook, nhưng nhấn bộ đếm này không ảnh hưởng đến bộ đếm kia: custom hook chia sẻ logic, không chia sẻ state. Thêm một hàm decrement vào useCounter và một nút gọi hàm đó, cả hai bộ đếm đều có ngay. Các pattern khác (useToggle, useDebounce, useLocalStorage) có trong trang custom hook.
Nên dùng hook nào
- Màn hình cần thay đổi khi một giá trị thay đổi:
useState, hoặcuseReducerkhi nhiều sự kiện cùng cập nhật một state. - Bạn cần làm việc với thứ gì đó nằm ngoài React (timer, subscription, request):
useEffect. - Bạn cần một DOM node hoặc một giá trị không hiển thị trên màn hình:
useRef. - Nhiều component nằm sâu trong cây cần cùng một giá trị:
useContext. - Một lần render chậm vì một phép tính hoặc vì một component con đã memo vẫn render lại:
useMemovàuseCallback. React Compiler có thể tự thêm phần memo này cho bạn, nhưng viết tay vẫn chạy và vẫn phổ biến. - Gõ phím thấy giật vì lần cập nhật phía sau quá nặng:
useTransitionhoặcuseDeferredValue.
Câu hỏi thường gặp
Hook trong React là gì?
Hook là những hàm có tên bắt đầu bằng use, cho phép function component dùng các tính năng của React: useState cho state, useEffect cho side effect, useContext cho context, useRef cho những giá trị tồn tại qua các lần render mà không gây ra render, và nhiều hook khác.
Vì sao React thêm hook?
Trước khi có hook (React 16.8), state và các phương thức lifecycle chỉ có trong class component, và chia sẻ logic có state phải dùng mixin, higher order component hoặc render props. Hook đưa logic đó vào những hàm thông thường mà bạn gọi được từ bất kỳ function component nào và chia sẻ dưới dạng custom hook.
React có bao nhiêu hook?
React 19.2 có khoảng hai mươi hook có sẵn trong package react, cộng thêm useFormStatus trong react-dom. Phần lớn ứng dụng dùng hằng ngày một vài hook: useState, useEffect, useRef, useContext, useMemo và useCallback.
Có dùng hook trong class component được không?
Không. Hook chỉ hoạt động bên trong function component và các hook khác. Bạn vẫn có thể render một class component bên cạnh các function component, và một class có thể render một function component dùng hook.
Custom hook là gì?
Là hàm do bạn tự viết, có tên bắt đầu bằng use và gọi các hook khác. Nó cho phép nhiều component dùng lại cùng một logic có state, trong khi mỗi component gọi nó vẫn có state riêng biệt.