Part 07 · Product Boundaries · 7.1.08

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.

Boundary mindset: chia ứng dụng theo vùng có thể load, fail, retry và navigate độc lập. Boundary tốt giữ phần còn lại sử dụng được, giải thích trạng thái và cung cấp next action an toà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

StateUI cần trả lờiAction
Loading đầu tiênCấu trúc nào đang đến?Skeleton/status có accessible name
EmptyKhông có dữ liệu hay filter loại hết?Create/reset filter
Partial/stalePhần nào vẫn đáng tin?Giữ content, báo refresh
OfflineAction đã lưu local hay chưa gửi?Retry/sync status
Unauthorized/forbiddenCần login hay thiếu quyền?Authenticate/request access
ErrorPhạ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.

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.

Authorization không nằm trong component. Ẩn nút hoặc chặn route phía client không ngăn attacker gọi API. Server phải authenticate, authorize object/action và validate input. Bất kỳ secret nào có trong client bundle, source map, storage hoặc network response đều xem như user có thể đọc.

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.

Review checklist: boundary đúng failure domain; event/async errors có owner; URL reload được; server enforce authorization; rich HTML được sanitize; secret không vào client; semantic/focus/live-region behavior được test ngoài lint.
Tài liệu: React Error Boundaries · dangerouslySetInnerHTML · OWASP Cross Site Scripting · Content Security Policy · WCAG 2.2 Quick Reference · ARIA Authoring Practices Guide