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ặcconst 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+Rtrên Windows/Linux,Cmd+Shift+Rtrê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-hooksphá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.

