Menu

React Hooks: giải thích mọi hook qua ví dụ chạy trực tiếp

Hook là một hàm có tên bắt đầu bằng use, cho phép component dùng một tính năng của React như state, context, ref hay effect. Xem mọi hook có sẵn được nhóm theo mục đích, hai quy tắc mà hook nào cũng tuân theo và cách tự viết hook của riêng bạn.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

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.

HookMang lại cho bạnTrang
useStateMột giá trị tồn tại qua các lần render và một hàm set để render lạiuseState
useReducerState được cập nhật bằng hàm reducer và các action được dispatchuseReducer
useContextMột giá trị do component cha cung cấp, không cần truyền propsuseContext
useRefMộ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 nodeuseRef
useImperativeHandleChọn những gì component cha thấy qua ref của bạnforwardRef
useEffectCode thường chạy sau khi vẽ: timer, subscription, fetch dữ liệuuseEffect
useLayoutEffectCode chạy sau khi DOM cập nhật nhưng trước khi vẽuseLayoutEffect
useInsertionEffectChạy trước layout effect, dành cho các thư viện CSS-in-JS
useEffectEventMột hàm mà effect có thể gọi, luôn thấy props và state mới nhấtuseEffect
useMemoCache một giá trị đã tính giữa các lần renderuseMemo
useCallbackCache một hàm giữa các lần renderuseCallback
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
useDeferredValueMột bản sao của giá trị, chậm lại phía sau khi render nặnguseDeferredValue
useIdMột id duy nhất, khớp nhau giữa máy chủ và máy kháchuseId
useSyncExternalStoreSubscribe vào một store nằm ngoài ReactuseSyncExternalStore
useDebugValueMộ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ệnuse
useActionStateState được một form action cập nhật, kèm cờ đang chờuseActionState
useOptimisticHiển thị kết quả mong đợi trước khi máy chủ trả lờiuseOptimistic
useFormStatusTrạ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:

  1. 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.
  2. 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ặc useReducer khi 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: useMemo và 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: useTransition hoặc useDeferredValue.

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.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu