Error recovery, routing, security và accessibility
Loading, empty, partial, offline, unauthorized và failure đều là product states. UI phải phục hồi đúng phạm vi, URL phải giữ navigation semantics, còn security và accessibility phải được bảo đảm ở boundary thật — không chỉ ở giao diện.
1. Error Boundary bắt được gì?
Error Boundary bắt lỗi render của descendant, bao gồm constructor và lifecycle tương ứng, rồi hiển thị fallback và có thể report telemetry. Nó không tự bắt lỗi trong event handler, callback async/timer, server rendering hay chính boundary đó. Những lỗi này cần được xử lý tại async/event boundary hoặc bởi framework runtime phù hợp.
class ProductErrorBoundary extends React.Component {
state = { failed: false };
static getDerivedStateFromError() {
return { failed: true };
}
componentDidCatch(error, info) {
reportError(error, { componentStack: info.componentStack });
}
render() {
return this.state.failed ? this.props.fallback : this.props.children;
}
}
Đặt boundary theo vùng product có thể phục hồi: route, panel editor, checkout section. Boundary ở root duy nhất tránh blank screen nhưng fallback quá rộng; boundary quanh từng button lại làm flow rời rạc. Retry phải reset state hoặc đổi reset key và chỉ lặp operation an toàn.
2. Thiết kế đầy đủ trạng thái UX
| State | UI cần trả lời | Action |
|---|---|---|
| Loading đầu tiên | Cấu trúc nào đang đến? | Skeleton/status có accessible name |
| Empty | Không có dữ liệu hay filter loại hết? | Create/reset filter |
| Partial/stale | Phần nào vẫn đáng tin? | Giữ content, báo refresh |
| Offline | Action đã lưu local hay chưa gửi? | Retry/sync status |
| Unauthorized/forbidden | Cần login hay thiếu quyền? | Authenticate/request access |
| Error | Phạm vi ảnh hưởng và correlation ID? | Retry, alternate path, support |
Không log rồi nuốt lỗi khiến UI treo ở pending. Thông báo user không được lộ stack, token hoặc internal identifiers nhạy cảm; telemetry cần context đủ để chẩn đoán nhưng phải redact dữ liệu cá nhân.
3. Routing là state có thể chia sẻ
URL nên encode resource, filter, sort, page và tab có ý nghĩa chia sẻ/bookmark. Route loaders/actions/boundaries gom data lifecycle theo navigation, cho phép prefetch và giảm waterfall. Back/forward, deep link và reload phải phục hồi cùng view thay vì phụ thuộc memory state.
- Cập nhật document title và heading chính theo route.
- Quản focus sau navigation để screen reader nhận context mới.
- Khôi phục scroll theo navigation semantics, không luôn ép về đầu trang.
- Pending navigation cần feedback nhưng vẫn giữ context khi transition cho phép.
- Route guard phía client chỉ cải thiện UX; server/API vẫn xác thực và phân quyền mỗi request.
4. XSS, trusted HTML và secret handling
React escape text interpolation trước khi tạo DOM, vì vậy render string trong JSX an toàn hơn tự ghép HTML. dangerouslySetInnerHTML bỏ lớp bảo vệ này; chỉ truyền content đã sanitize bằng policy đáng tin, không dùng regex tự chế. URL, CSS, script context và third-party widget vẫn có sink riêng cần threat model.
Content Security Policy, Trusted Types, dependency review, lockfile scanning và output encoding là defense-in-depth. Tránh lưu long-lived credential trong localStorage nếu architecture cho phép cookie HttpOnly/Secure/SameSite; lựa chọn cụ thể phải xét CSRF, XSS và deployment model.
5. Accessibility từ semantic đến focus
Dùng native semantic elements trước ARIA: button cho action, link cho navigation, label cho control, heading theo hierarchy. Mọi interactive element phải dùng được bằng keyboard, có accessible name và focus indicator thấy rõ. Không dựa riêng vào màu; tôn trọng contrast, zoom, reflow và prefers-reduced-motion.
Dynamic status như save success có thể dùng live region với mức priority phù hợp; validation error phải được liên kết tới field và summary. Dialog cần focus trap đúng, Escape/close semantics và trả focus. Virtualized list, drag-and-drop và custom widget cần keyboard model cùng screen-reader feedback có chủ đích.
6. Kiểm thử boundary thực tế
Lint chỉ tìm một phần lỗi. Test keyboard order, visible focus, accessible tree/name/role, zoom và screen reader trên flow quan trọng. Inject render error để kiểm fallback/report/reset; đảo thứ tự network và status code; test deep link, reload, back/forward, unauthorized API call và sanitized rich content.