Tối thiểu bốn trạng thái: đang tải, có dữ liệu, rỗng, lỗi.
Bỏ sót trạng thái rỗng là lỗi hay gặp nhất — danh sách không có dữ liệu trông y hệt lỗi im lặng.
jsx
if (isLoading) return <ListSkeleton rows={6} />
if (isError) return <ErrorState message={error.message} onRetry={refetch} />
if (items.length === 0) return <EmptyState onCreate={openCreateModal} />
return <List items={items} />Cách trình bày từng trạng thái:
- Loading: dùng skeleton đúng hình dạng nội dung sắp hiện cho danh sách/thẻ, spinner cho vùng nhỏ. Skeleton giữ chỗ nên trang không nhảy khi dữ liệu về.
- Empty: nói rõ vì sao rỗng và bước tiếp theo. "Chưa có đơn hàng nào — Tạo đơn đầu tiên" khác hẳn "Không có dữ liệu". Phân biệt rỗng thật với rỗng do bộ lọc (khi đó gợi ý xoá bộ lọc).
- Error: thông báo người dùng hiểu được kèm nút "Thử lại", không in thẳng stack trace. Phân biệt lỗi mạng (thử lại được) với 4xx (thử lại vô ích).
Một điểm nữa hay được hỏi: phân biệt tải lần đầu với tải lại nền. Lần đầu thì hiện skeleton; khi làm mới dữ liệu đã có thì giữ nội dung cũ, chỉ thêm chỉ báo nhỏ — tháo nội dung ra để thay bằng spinner khiến giao diện nhấp nháy.