Sửa lỗi "Invalid hook call. Hooks can only be called inside of the body of a function component." trong React

beginner⚛️ React2026-03-21| React 16.8+, mọi hệ điều hành, mọi bundler (Vite, Webpack, CRA, Next.js)

Error Message

Invalid hook call. Hooks can only be called inside of the body of a function component.
#react#hooks#rules-of-hooks#function-component

Chuyện gì vừa xảy ra

React app của bạn đang chạy tốt — rồi đột nhiên màn hình trắng. Mở console lên và bạn thấy:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

Đây là lỗi crash hoàn toàn, không phải cảnh báo. React từ chối render bất cứ thứ gì cho đến khi bạn sửa xong.

Có ba nguyên nhân gây ra lỗi này. Hãy kiểm tra từng cái theo thứ tự — cái đầu tiên là bẫy mà hầu hết lập trình viên đều mắc phải.

Nguyên nhân 1: Hai bản sao React trong project của bạn

Phổ biến hơn bạn nghĩ, đặc biệt sau khi cài một thư viện UI như Material UI, Ant Design, hoặc một package nội bộ bạn đang tự xây dựng.

Cách kiểm tra

Chạy lệnh này tại thư mục gốc của project:

npm ls react

Hoặc với pnpm/yarn:

pnpm why react
yarn why react

Thấy React xuất hiện ở hai đường dẫn khác nhau? Đó chính là vấn đề. App của bạn chạy một bản. Thư viện chạy bản kia. Khi thư viện gọi hook, React không tìm thấy context trạng thái chung — crash.

Cách sửa

Nếu bản trùng lặp đến từ một package nội bộ bạn đang phát triển (được symlink qua npm link hoặc monorepo workspace), hãy trỏ thư viện đó về React của app. Trong webpack config của thư viện:

// webpack.config.js (trong thư viện của bạn)
const path = require('path');

module.exports = {
  // ...
  resolve: {
    alias: {
      react: path.resolve('./node_modules/react'),
    },
  },
};

Với Vite:

// vite.config.js (trong thư viện của bạn)
import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig({
  resolve: {
    alias: {
      react: path.resolve('./node_modules/react'),
    },
  },
});

Nếu bản trùng lặp đến từ một package bên thứ ba, thường do peer dependencies không khớp phiên bản. Hãy cập nhật React và cài lại từ đầu:

npm install react@latest react-dom@latest
rm -rf node_modules package-lock.json
npm install

Nguyên nhân 2: Gọi hook bên ngoài function component

useState, useEffect, useRef — bất kỳ hàm nào có tiền tố use — phải được gọi trực tiếp bên trong function component hoặc custom hook. Chỉ vậy thôi. Gọi ở bất kỳ nơi nào khác và React sẽ ném lỗi này.

Các lỗi thường gặp

Hook bên trong hàm thông thường:

// SAI — hàm thông thường, không phải component
function fetchUserData(userId) {
  const [data, setData] = useState(null); // Error!
  // ...
}

// ĐÚNG — chuyển hook vào bên trong component
function UserProfile({ userId }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchUserData(userId).then(setData);
  }, [userId]);

  return {data?.name};
}

Hook bên trong event handler:

// SAI
function LoginButton() {
  function handleClick() {
    const [token, setToken] = useState(''); // Error!
  }

  return Login;
}

// ĐÚNG — state đặt ở cấp độ component
function LoginButton() {
  const [token, setToken] = useState('');

  function handleClick() {
    setToken('abc123');
  }

  return Login;
}

Hook bên trong câu điều kiện:

// SAI — hook không được đặt trong if/else
function Dashboard({ isLoggedIn }) {
  if (isLoggedIn) {
    const [profile, setProfile] = useState(null); // Error!
  }
}

// ĐÚNG — luôn gọi hook; xử lý điều kiện dựa trên giá trị trả về
function Dashboard({ isLoggedIn }) {
  const [profile, setProfile] = useState(null);

  useEffect(() => {
    if (isLoggedIn) {
      fetchProfile().then(setProfile);
    }
  }, [isLoggedIn]);
}

Nguyên nhân 3: Hooks bên trong class component

Hooks không hoạt động trong class component — hoàn toàn không. Điều này thường xảy ra khi đang migration dở dang: người ta bắt đầu thêm useState mà chưa chuyển đổi class trước.

// SAI — class component + hooks = lỗi
class UserCard extends React.Component {
  render() {
    const [count, setCount] = useState(0); // Error!
    return {count};
  }
}

// ĐÚNG — chuyển sang function component
function UserCard() {
  const [count, setCount] = useState(0);
  return {count};
}

Chưa thể chuyển đổi toàn bộ class ngay bây giờ? Hãy tách logic hook ra thành một function component con nhỏ và render nó bên trong class. Không đẹp lắm, nhưng giúp bạn tiếp tục được.

Danh sách kiểm tra nhanh

  • Chạy npm ls react — có nhiều hơn một phiên bản được liệt kê không?
  • Hook có đặt ở cấp độ cao nhất của function component không — không nằm trong if, for, hay hàm lồng nhau?
  • Component có phải là function (function Foo() hoặc const Foo = () =>) không, chứ không phải class?
  • Tên component có bắt đầu bằng chữ hoa không? React dùng điều này để phân biệt component với hàm thông thường.
  • Bạn có đang gọi một custom hook mà bên trong nó lại gọi hook khác từ context không hợp lệ không?

Xác nhận đã sửa xong

Sau khi thực hiện thay đổi:

  • Xóa sạch console trình duyệt.
  • Hard-refresh (Ctrl+Shift+R trên Windows/Linux, Cmd+Shift+R trên Mac).
  • Lỗi Invalid hook call sẽ biến mất.
  • Component render bình thường không bị crash.

Muốn phát hiện lỗi này trước khi lên trình duyệt? Hãy cài ESLint plugin:

npm install --save-dev eslint-plugin-react-hooks

Thêm vào ESLint config của bạn:

// .eslintrc.json
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

Plugin sẽ đánh dấu các lỗi gọi hook ngay khi bạn gõ — không cần chạy app mới phát hiện ra.

Tóm tắt

  • Lỗi xuất hiện ngay sau khi cài thư viện? Kiểm tra bản sao React trùng lặp trước. Đây là nguyên nhân khó phát hiện nhất và cũng phổ biến nhất.
  • Hooks phải đặt ở cấp độ cao nhất của function component hoặc custom hook — không điều kiện, không vòng lặp, không hàm lồng nhau.
  • eslint-plugin-react-hooks phát hiện cả ba nguyên nhân ngay lúc lint. Cài một lần và không bao giờ phải debug lỗi này nữa.

Related Error Notes