Chia vai rõ: Suspense bắt trạng thái "chưa có dữ liệu" (promise pending), Error Boundary bắt trạng thái "hỏng" (promise reject hoặc lỗi khi render).
Một promise bị reject sẽ đi xuyên qua Suspense và dừng ở Error Boundary gần nhất phía trên.
jsx
<ErrorBoundary fallback={<RetryPanel />}>
<Suspense fallback={<Skeleton />}>
<Comments promise={commentsPromise} />
</Suspense>
</ErrorBoundary>Đặt Error Boundary bọc ngoài Suspense là layout thường dùng: lỗi thì cả vùng đó chuyển sang màn hình lỗi thay vì kẹt ở skeleton mãi.
Để thử lại, cần hai việc: reset boundary và tạo lại resource. Vì promise cũ đã reject vĩnh viễn, chỉ reset boundary mà dùng lại promise đó sẽ lỗi ngay lần nữa. Cách phổ biến là đổi key để cả cây con mount lại với promise mới:
jsx
const [attempt, setAttempt] = useState(0)
<ErrorBoundary key={attempt} fallback={<button onClick={() => setAttempt((n) => n + 1)}>Retry</button>}>
...
</ErrorBoundary>Lưu ý: Error Boundary chỉ bắt lỗi trong render/lifecycle của cây con.
- Lỗi trong event handler,
setTimeout, hay promise không được React theo dõi thì phảitry/catchthủ công. - Trong Next.js App Router,
error.tsxchính là boundary sẵn có kèm hàmreset().