Tóm tắt nhanh
Bạn đang gọi .map() trên một giá trị undefined. Cách sửa nhanh — thêm giá trị fallback:
// Lỗi crash
{items.map(item => <li key={item.id}>{item.name}</li>)}
// An toàn
{(items ?? []).map(item => <li key={item.id}>{item.name}</li>)}
Thông báo lỗi đầy đủ
TypeError: Cannot read properties of undefined (reading 'map')
at ProductList (ProductList.jsx:12)
at renderWithHooks (react-dom.development.js:14985)
React render component theo kiểu đồng bộ. Nếu products là undefined ngay lần render đầu tiên — dù chỉ trong tích tắc — thì lời gọi .map() sẽ bùng nổ ngay lập tức. Không có khoảng thời gian ân hạn nào.
Nguyên nhân gốc rễ
1. State khởi tạo bằng undefined thay vì mảng rỗng
// Sai — useState() không có đối số mặc định sẽ là undefined
const [products, setProducts] = useState();
// Đúng
const [products, setProducts] = useState([]);
2. Dữ liệu từ API chưa được tải về
useEffect chạy sau lần render đầu tiên. Vì vậy ở lần hiển thị ban đầu, state vẫn là giá trị bạn đã khởi tạo — và có thể là không có gì cả.
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => setProducts(data)); // đến SAU lần render đầu tiên
}, []);
3. Tên props khác nhau ở component cha và con
// Component cha truyền 'items'
<ProductList items={products} />
// Component con đọc 'data' — giá trị này là undefined
function ProductList({ data }) {
return data.map(...); // crash
}
4. Cấu trúc dữ liệu từ API không khớp
// Bạn kỳ vọng: [{ id: 1, name: 'Widget' }, ...]
// API thực tế trả về: { data: [...], total: 42 }
setProducts(response); // gán products thành object, không phải mảng
Cái này khiến mọi người vấp phải liên tục. Hãy luôn log response thô ra trước khi giả định cấu trúc của nó.
Các bước sửa lỗi
Cách 1: Khởi tạo state bằng mảng rỗng
Đây là nguyên nhân phổ biến nhất. Đừng bao giờ để state kiểu mảng bắt đầu bằng undefined.
const [products, setProducts] = useState([]); // ✅
Cách 2: Thêm fallback trước khi map
Ba lựa chọn đáng tin cậy tùy theo mức độ phòng thủ bạn muốn:
// Lựa chọn A: nullish coalescing (gọn nhất cho undefined/null)
{(products ?? []).map(product => (
<li key={product.id}>{product.name}</li>
))}
// Lựa chọn B: optional chaining (không render gì nếu undefined)
{products?.map(product => (
<li key={product.id}>{product.name}</li>
))}
// Lựa chọn C: Array.isArray (rõ ràng nhất)
{Array.isArray(products) && products.map(product => (
<li key={product.id}>{product.name}</li>
))}
Hãy dùng Array.isArray() khi giá trị có thể là object, null, hay số — không chỉ riêng undefined. Đây là lựa chọn chắc chắn nhất trong ba cách.
Cách 3: Theo dõi trạng thái loading cho dữ liệu bất đồng bộ
function ProductList() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, []);
if (loading) return <p>Đang tải...</p>;
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
Cách 4: Kiểm tra response từ API trước khi sử dụng
fetch('/api/products')
.then(res => res.json())
.then(data => {
console.log(data); // luôn log trước khi giả định cấu trúc
// API bọc kết quả dạng { data: [...], total: 42 }?
setProducts(data.data ?? data);
});
Cách 5: Kiểm tra kiểu props trong quá trình phát triển
Phát hiện tên prop sai trước khi lên production — thay vì chờ đến khi nhận được bug report khó hiểu.
// PropTypes
import PropTypes from 'prop-types';
ProductList.propTypes = {
items: PropTypes.array.isRequired,
};
ProductList.defaultProps = {
items: [],
};
// TypeScript — kiểm tra tại compile-time, không tốn chi phí runtime
interface ProductListProps {
items: Product[];
}
function ProductList({ items }: ProductListProps) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Checklist gỡ lỗi
- Mở DevTools và kiểm tra stack trace — nó chỉ ra chính xác file và dòng code xảy ra crash
- Thêm
console.log(yourVariable)ngay trước.map()và xem giá trị thực sự của nó ở lần render đầu tiên là gì - Kiểm tra lời gọi
useState— giá trị khởi tạo làundefinedhay[]? - So sánh tên prop ở component cha (nơi truyền vào) với component con (nơi destructure) — chỉ cần một lỗi đánh máy là đủ gây sự cố
- Log response thô từ API trước khi gọi
setStateđể xác nhận mảng nằm đúng chỗ bạn mong đợi
Xác nhận đã sửa xong
Bốn điều cần kiểm tra sau khi áp dụng bản sửa lỗi:
- Mở console trình duyệt — lỗi
TypeErrorphải biến mất. - Danh sách hiển thị đúng với dữ liệu thực.
- Chuyển sang Slow 3G trong DevTools → tab Network để kiểm tra stress-test luồng bất đồng bộ.
- Thử với response trả về mảng rỗng — không crash, chỉ hiển thị danh sách trống.
Bảng tham khảo nhanh
| Nguyên nhân | Cách sửa |
|------------------------------------|---------------------------------------------------|
| useState() không có giá trị khởi tạo | useState([]) |
| Dữ liệu async ở lần render đầu | loading state + render có điều kiện |
| Tên prop sai | khớp tên prop ở component cha và con |
| API trả về object, không phải mảng | setProducts(response.data ?? response) |
| Prop có thể null từ component cha | defaultProps hoặc fallback ?? [] |

