React & Frontend7 phút đọc · 3 thg 6, 2026

React Hooks là gì? Hiểu useState và useEffect qua ví dụ thực tế

✦ Tóm tắt bởi AI

Bài giới thiệu React Hooks là các hàm cho phép function component dùng state và lifecycle như class component nhưng code ngắn gọn hơn. Hai hooks chính là useState để quản lý state và useEffect để xử lý side effects, cùng hai quy tắc bắt buộc về thứ tự gọi và nơi gọi hooks. Nắm vững hai hooks này cộng các mẹo tránh lỗi và lựa chọn hook phù hợp là chìa khóa để thành thạo React hiện đại.

HOLETEX · POST
REACT HOOKS
useState · useEffect

Nếu bạn học React được một thời gian, kiểu gì cũng gặp useStateuseEffect. Đây là hai hooks dùng nhiều nhất, và hiểu chúng là bước ngoặt từ "biết viết component" sang "thật sự làm chủ React". Bài này giải thích React Hooks là gì, vì sao ra đời, rồi đi sâu vào useState và useEffect bằng code ví dụ chạy được, kèm những lỗi người mới hay mắc.

Nếu bạn chưa rõ React là cái gì, đọc trước bài React là gì? rồi quay lại đây.

React Hooks là gì

Hooks là các hàm cho phép bạn dùng những tính năng của React (như state và vòng đời component) ngay bên trong một function component. Tên hooks luôn bắt đầu bằng use: useState, useEffect, useRef, useContext...

Trước đây, muốn có state hay xử lý vòng đời, bạn bắt buộc phải viết class component với this.state, componentDidMount, componentDidUpdate... Function component chỉ là hàm "câm", nhận props và trả về giao diện, không giữ được dữ liệu riêng. Hooks ra đời (từ React 16.8) để function component cũng làm được mọi thứ class làm được, mà code ngắn và dễ đọc hơn nhiều.

Đội ngũ React nói rõ trong tài liệu: class component vẫn được hỗ trợ, không bị deprecated, nhưng họ khuyến nghị code mới nên viết bằng function component với hooks. Nói cách khác, hooks giờ là cách làm mặc định của React.

Vì sao hooks tốt hơn class trong đa số trường hợp:

  • Bỏ được this rối rắm, bỏ binding hàm trong constructor.
  • Gom logic liên quan vào một chỗ thay vì rải rác qua nhiều lifecycle method.
  • Dễ tách logic tái sử dụng thành custom hooks (hàm riêng của bạn bắt đầu bằng use).

Quy tắc hooks (Rules of Hooks)

Hooks có hai quy tắc bắt buộc. Vi phạm là app chạy sai một cách khó đoán, nên nắm chắc từ đầu.

Quy tắc 1: Chỉ gọi hooks ở top level. Không gọi hooks bên trong vòng lặp, câu lệnh điều kiện, hàm lồng nhau, hay trong khối try/catch/finally. Luôn gọi ở đầu thân function component, trước mọi câu return sớm.

jsx
function Profile() {
  // ✅ Đúng: top level
  const [name, setName] = useState('');

  // ❌ Sai: trong if
  if (name) {
    const [age, setAge] = useState(0);
  }
}

Lý do: React dựa vào thứ tự gọi hooks để khớp đúng state qua mỗi lần render. Nếu lần này gọi 3 hooks, lần sau điều kiện làm nó chỉ gọi 2, mọi thứ lệch hết.

Quy tắc 2: Chỉ gọi hooks từ function của React. Tức là từ function component hoặc từ custom hook của bạn, không gọi từ hàm JavaScript thường.

Bạn không cần thuộc lòng: cài eslint-plugin-react-hooks là editor tự cảnh báo khi bạn vi phạm.

useState: thêm state cho component

useState khai báo một biến state mà component "nhớ" qua các lần render. Cú pháp:

jsx
const [state, setState] = useState(initialState);

Nó trả về một mảng 2 phần tử: giá trị state hiện tại, và một hàm để cập nhật state đó. Gọi hàm set sẽ kích hoạt React render lại component.

Ví dụ counter kinh điển:

jsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Đã bm {count} ln
    </button>
  );
}

State là một snapshot. Đây là điểm người mới hay vấp. Gọi setState không đổi biến state ngay trong dòng code đang chạy:

jsx
function handleClick() {
  setCount(count + 1);
  console.log(count); // vẫn in ra giá trị CŨ
}

Giá trị mới chỉ xuất hiện ở lần render kế tiếp. React cũng gộp (batch) nhiều lần cập nhật trong cùng một event handler rồi mới vẽ lại màn hình một lần.

Vì vậy, khi cập nhật state dựa trên giá trị trước đó, hãy dùng updater function (dạng prev => ...):

jsx
function handleClick() {
  setCount(c => c + 1);
  setCount(c => c + 1);
  setCount(c => c + 1); // count tăng đúng 3
}

Nếu viết setCount(count + 1) ba lần, cả ba đều đọc cùng một count cũ, kết quả chỉ +1.

Một lưu ý quan trọng nữa: không mutate object/array trong state, luôn tạo bản mới:

jsx
// ❌ Sai: sửa trực tiếp
form.firstName = 'Lan';
setForm(form);

// ✅ Đúng: tạo object mới
setForm({ ...form, firstName: 'Lan' });

useEffect: đồng bộ với hệ thống bên ngoài

useEffect cho phép chạy "side effect": những việc nằm ngoài quá trình render thuần như gọi API, đăng ký sự kiện, thao tác timer, kết nối WebSocket. Cú pháp:

jsx
useEffect(setup, dependencies?);

Effect chạy sau khi React commit thay đổi lên DOM (sau khi render xong), không phải trong lúc render.

Dependency array quyết định khi nào effect chạy lại:

DependenciesEffect chạy khi nào
Không truyềnSau mỗi lần render
Mảng rỗng []Chỉ một lần sau lần mount đầu tiên
[a, b]Sau mount, rồi mỗi khi a hoặc b đổi (so sánh bằng Object.is)

Cleanup function: setup có thể trả về một hàm dọn dẹp. Hàm này chạy trước khi effect chạy lại (khi dependency đổi) và chạy một lần khi component unmount. Dùng để hủy đăng ký, đóng kết nối, clear timer.

Ví dụ đầy đủ có cleanup, kết nối phòng chat:

jsx
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    // setup: mở kết nối
    const connection = createConnection(serverUrl, roomId);
    connection.connect();

    // cleanup: đóng kết nối khi roomId/serverUrl đổi hoặc khi unmount
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);

  return <h1>Chào mng ti {roomId}!</h1>;
}

Các lỗi thường gặp với useEffect:

  • Thiếu dependency: quên đưa biến mà effect dùng vào mảng. Effect không phản ứng khi biến đổi, sinh ra "stale closure" (đọc giá trị cũ). Hãy liệt kê đủ mọi prop/state mà effect dùng.
  • Quên mảng dependency: effect chạy sau mỗi render, dễ thành vòng lặp gọi API vô tận nếu trong effect lại set state.
  • Quên cleanup: rò rỉ bộ nhớ, đăng ký trùng, timer chồng nhau.
  • Object/function làm dependency: chúng tạo reference mới mỗi lần render nên effect chạy lại liên tục. Cách xử lý: chuyển việc tạo object/function vào trong effect, hoặc bọc bằng useMemo/useCallback.

Một mẹo: trong dev với Strict Mode, React cố tình chạy setup → cleanup → setup để test xem cleanup của bạn có đúng không. Thấy effect chạy hai lần lúc dev là bình thường, không phải bug.

Vài hook khác hay dùng

Khi vững useState và useEffect, bạn sẽ gặp thêm:

  • useRef: giữ một giá trị "đời sống" qua các lần render mà không làm re-render khi đổi. Hay dùng để tham chiếu tới một DOM node (ví dụ focus vào ô input), hoặc lưu giá trị tạm như id của timer.
  • useContext: đọc dữ liệu từ một Context, tránh phải truyền props qua nhiều tầng (prop drilling). Hợp với theme, thông tin user đăng nhập, ngôn ngữ.
  • useMemo: cache (ghi nhớ) kết quả một phép tính nặng, chỉ tính lại khi dependency đổi.
  • useCallback: cache định nghĩa một function, hữu ích khi truyền callback xuống component con đã tối ưu để tránh nó render thừa.
  • useReducer: như useState nhưng gom logic cập nhật vào một hàm reducer, hợp khi state phức tạp nhiều nhánh.

Đừng vội nhồi useMemo/useCallback vào mọi chỗ. Chỉ tối ưu khi đo được là chậm, dùng bừa còn làm code khó đọc hơn.

Mẹo dùng hooks cho đúng

  • Bật eslint-plugin-react-hooks ngay từ đầu, nó bắt gần hết lỗi quy tắc hooks và dependency thiếu.
  • Khi cập nhật state dựa trên giá trị cũ, mặc định dùng updater function prev => ....
  • Không nhét hết mọi thứ vào một useEffect khổng lồ. Mỗi effect lo một việc, đặt đủ dependency cho từng cái.
  • Tự hỏi "việc này có thật sự cần effect không". Nhiều logic tính toán từ props/state nên tính thẳng khi render, không cần useEffect.
  • Tách logic lặp lại thành custom hook (useXxx) để tái sử dụng cho gọn.

Tiếp theo học gì

Hiểu useState và useEffect là bạn đã nắm phần lõi của React hiện đại. Bước tiếp theo là luyện qua dự án thật để biết khi nào dùng hook nào cho đúng. Xem Lộ trình học ReactJS để biết thứ tự học hợp lý từ nền JavaScript tới đi làm.


### Hiểu hooks là bước ngoặt khi học React >Đọc lý thuyết là một chuyện, biết khi nào dùng useState, khi nào cần useEffect, khi nào nên tránh effect thừa lại là chuyện khác, và chỉ làm dự án thật mới rèn được. Khóa React PRO của HoleTex dạy hooks theo đúng cách bạn sẽ dùng khi đi làm: từ căn bản đến state management, kèm dự án hoàn chỉnh và review code. >Xem khóa React PRO của HoleTex →

Bài liên quan

Nguồn tham khảo: react.dev/reference/react/hooks, react.dev/reference/react/useState, react.dev/reference/react/useEffect, react.dev/reference/rules/rules-of-hooks, react.dev/reference/react/Component. Đã đối chiếu 2026-06-04.

Thấy hay? Chia sẻ