Error Boundary là một component React bắt các lỗi JavaScript trong cây component con và hiển thị giao diện dự phòng thay vì trang sự cố. Trong bối cảnh React Native, nó ngăn chặn sự cố hoàn toàn của ứng dụng trong các lỗi hiển thị không nghiêm trọng. Theo Tài liệu React, 2024, error boundary bắt lỗi trong các phương thức render, hook vòng đời và hàm tạo của component con, cho phép ứng dụng tiếp tục hoạt động. Trong React Native, điều này đặc biệt quan trọng vì ứng dụng di động không thể tải lại bằng F5 — người dùng mất toàn bộ phiên làm việc.
Điểm chính
Error Boundary là một cơ chế React để suy thoái từ từ khi xảy ra lỗi hiển thị. Được giới thiệu trong React 16 (2017) như một component bao bọc thực hiện một trong hai phương thức vòng đời: static getDerivedStateFromError hoặc componentDidCatch. Error Boundary cho phép hiển thị cho người dùng một thông báo có ý nghĩa thay vì màn hình trắng hoặc sự cố hoàn toàn ứng dụng.
Trước React 16, bất kỳ lỗi không được xử lý nào trong render sẽ gây ra sự cố hoàn toàn ứng dụng với việc tháo gỡ toàn bộ cây DOM. Trong ứng dụng web, điều này có nghĩa là màn hình trắng; trong React Native, sự cố hoàn toàn và quay trở lại Màn hình chính. Nhóm React đã giới thiệu Error Boundary như một phần tương tự của khối catch cho UI khai báo, mượn khái niệm từ cách tiếp cận “let it crash” trong ngôn ngữ Erlang.
Trong React Native, việc thiếu Error Boundary có nghĩa là sự cố hoàn toàn ứng dụng khi xảy ra bất kỳ lỗi hiển thị nào. Người dùng mất toàn bộ phiên hiện tại mà không có khả năng khôi phục. Error Boundary trong React Native rất quan trọng vì ứng dụng di động không thể tải lại như trang web — phiên làm việc của người dùng bị mất không thể khôi phục và người dùng phải bắt đầu lại từ đầu.
Error Boundary hoạt động ở cấp độ cây React. Khi một component con ném lỗi trong render hoặc vòng đời, React không tháo gỡ toàn bộ cây mà chuyển quyền điều khiển cho Error Boundary gần nhất trong hệ thống phân cấp. Boundary gọi getDerivedStateFromError, đặt state.hasError = true và hiển thị giao diện dự phòng thay cho nhánh component bị lỗi.
import React, { Component, ErrorInfo, ReactNode } from "react"
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error("Caught by boundary:", error)
Crashlytics.recordException(error)
}
render() {
if (this.state.hasError) {
return this.props.fallback || <FallbackUI />
}
return this.props.children
}
}
getDerivedStateFromError thiết lập trạng thái để hiển thị giao diện dự phòng — đây là một phương thức tĩnh được gọi trong giai đoạn hiển thị trước khi xác nhận thay đổi. componentDidCatch được thực thi trong giai đoạn xác nhận và dành cho các tác dụng phụ: ghi log, gửi báo cáo sự cố đến Crashlytics, phân tích. Hai phương thức phân chia trách nhiệm giữa quản lý trạng thái UI và các tác dụng phụ.
Để tạo Error Boundary, bạn cần triển khai một component lớp với các phương thức getDerivedStateFromError và/hoặc componentDidCatch. Các component hàm không thể là Error Boundary — React chỉ hỗ trợ chức năng này cho component lớp vì cần truy cập vào các phương thức vòng đời. Thư viện react-error-boundary cung cấp một triển khai có sẵn với API dựa trên hook để thuận tiện.
// Ví dụ sử dụng Error Boundary trong React Native
import { ErrorBoundary } from "react-error-boundary"
const FallbackComponent = ({ error, resetError }: FallbackProps) => (
<View style={styles.container}>
<Text>Đã xảy ra lỗi</Text>
<Text>{error.message}</Text>
<Button title="Thử lại" onPress={resetError} />
</View>
)
const App = () => (
<SafeAreaView>
<ErrorBoundary FallbackComponent={<FallbackComponent />}>
<UserProfile userId={"123"} />
</ErrorBoundary>
<BottomNavigation />
</SafeAreaView>
)
Cấp độ bao bọc — Error Boundary có thể được đặt ở các cấp độ khác nhau trong hệ thống phân cấp. Một Boundary toàn cục ở gốc ứng dụng sẽ hiển thị giao diện dự phòng khi có lỗi, nhưng điều hướng sẽ được giữ nguyên. Nhiều Boundary ở cấp độ màn hình cho phép cô lập lỗi: nếu một màn hình bị lỗi, các màn hình khác tiếp tục hoạt động độc lập. react-error-boundary đơn giản hóa cơ chế đặt lại thông qua hook useErrorBoundary, cho phép đặt lại trạng thái mà không cần tải lại. Đối với các dự án điển hình, sơ đồ Boundary ba cấp được coi là tối ưu cho ứng dụng React Native.
Sau khi lỗi xảy ra, người dùng có thể nhấp vào nút “Thử lại”, nút này đặt lại hasError thành false và hiển thị lại cây component con. Cơ chế đặt lại rất quan trọng để khôi phục chức năng ứng dụng mà không cần tải lại. Trong react-error-boundary, callback onReset được sử dụng, có thể xóa bộ nhớ đệm, lấy lại dữ liệu hoặc cập nhật trạng thái ở cấp cao hơn trong cây.
Error Boundary không bắt lỗi bất đồng bộ — lỗi trong setTimeout, setInterval, Promise, async/await. React không thể chặn các lỗi bên ngoài chu kỳ hiển thị và hook vòng đời vì chúng được thực thi trong các ngữ cảnh thực thi khác nhau. Đối với lỗi bất đồng bộ, cần có try-catch riêng trong các trình xử lý hoặc một trình xử lý sự kiện unhandledrejection toàn cục.
Các lỗi trong onClick, onChange và các trình xử lý sự kiện khác không bị Error Boundary bắt vì chúng được thực thi bên ngoài quá trình hiển thị React. Xử lý lỗi cho các trình xử lý sự kiện phải được thực hiện bên trong chính trình xử lý thông qua try-catch. Thư viện react-error-boundary cung cấp hook useErrorHandler để ném lỗi từ các trình xử lý sự kiện đến Boundary gần nhất.
Error Boundary không hoạt động ở phía máy chủ trong Next.js hoặc Gatsby. Các phương thức getDerivedStateFromError và componentDidCatch không được gọi trong SSR vì các phương thức vòng đời chỉ khả dụng trong trình duyệt. Đối với lỗi máy chủ, cần một chiến lược riêng: try-catch trong getServerSideProps, trang dự phòng error.js (Next.js 13+), hoặc middleware toàn cục.
Trong React Native, Error Boundary không ngăn chặn sự cố ở cấp độ module native. Sự cố native (segfault, hết bộ nhớ, ngoại lệ native) xảy ra ở cấp độ Objective-C hoặc Java và không chạm đến lớp JavaScript. Đối với sự cố native, cần Crashlytics NDK (Android) hoặc KSCrash (iOS). Error Boundary chỉ bảo vệ lớp JavaScript của ứng dụng React Native.
Đặt Error Boundary ở ranh giới của các module logic: một Boundary cho mỗi màn hình, một cho mỗi widget bên thứ ba, một cho mỗi biểu mẫu phức tạp. Điều này cô lập các lỗi và cho phép người dùng tiếp tục làm việc ở các phần khác của ứng dụng. Một Boundary gốc phải luôn có mặt — cho các lỗi nghiêm trọng trong các component điều hướng chung hoặc nhà cung cấp. Mỗi Boundary chịu trách nhiệm cho phần giao diện của riêng mình và không ảnh hưởng đến các component lân cận khi xảy ra lỗi.
Luôn chuyển lỗi đến Crashlytics hoặc Sentry thông qua componentDidCatch. Thêm ngữ cảnh: tên màn hình, userId, phiên bản ứng dụng, tham số điều hướng. Trong Sentry, breadcrumbs có sẵn — một chuỗi các hành động của người dùng dẫn đến lỗi. Để phân tích tần suất lỗi không nghiêm trọng, hãy sử dụng bảng điều khiển Crashlytics với phân nhóm theo vấn đề.
Đừng sử dụng fallback trần trụi — hãy tạo một giao diện có ý nghĩa. Bộ đề xuất cho React Native: thông báo lỗi (thân thiện với người dùng, không kỹ thuật), nút “Thử lại”, liên kết đến hỗ trợ hoặc trò chuyện. Tránh View trống — người dùng sẽ nghĩ ứng dụng đã bị sự cố hoàn toàn và sẽ đóng nó. Fallback phải được tích hợp vào thiết kế tổng thể của ứng dụng.
Kiểm thử từng Error Boundary bằng React Testing Library hoặc React Native Testing Library. Tạo một component kích hoạt ném lỗi khi hiển thị và xác nhận rằng giao diện dự phòng được hiển thị. Đối với kiểm thử tích hợp, hãy sử dụng storybook với các trạng thái Error Boundary khác nhau: bình thường, lỗi, trạng thái sau đặt lại. Kiểm thử tự động của Boundary đảm bảo rằng khi component thay đổi, giao diện dự phòng tiếp tục hoạt động chính xác trong môi trường sản xuất. Phần trăm bao phủ kiểm thử cho mỗi Boundary phải là yêu cầu bắt buộc trong đánh giá mã nguồn cho các dự án React Native.
Câu hỏi thường gặp
React triển khai Error Boundary chỉ thông qua component lớp vì cần truy cập vào các phương thức vòng đời componentDidCatch và getDerivedStateFromError. Các component hàm không có các phương thức như vậy. Thư viện react-error-boundary cung cấp một lớp bọc lớp có sẵn với API dựa trên hook để dễ sử dụng.
Ảnh hưởng là tối thiểu — Error Boundary thêm một kiểm tra trạng thái mỗi khi hiển thị cây component con. So sánh state.hasError là một thao tác O(1) với độ phức tạp hằng số. Khi không có lỗi, không có chi phí. Chỉ khi lỗi xảy ra, Boundary mới thực hiện thêm một lần hiển thị giao diện dự phòng.
Không, 2–3 cấp là đủ: một Boundary gốc cho toàn bộ ứng dụng, một Boundary màn hình cho mỗi nhánh điều hướng và một Boundary cục bộ cho các widget quan trọng (biểu mẫu thanh toán, bản đồ, trò chuyện). Việc sử dụng quá nhiều Boundary làm phức tạp kiến trúc mà không có lợi ích đáng kể.
Error Boundary và Suspense độc lập: Suspense bắt tải (Promise đang chờ trong React 18+), Error Boundary bắt lỗi hiển thị. Chúng có thể được kết hợp: <ErrorBoundary><Suspense><Component /></Suspense></ErrorBoundary>. Suspense kích hoạt trước khi tải, Error Boundary kích hoạt khi lỗi của component đã tải.
try-catch bắt lỗi trong mã mệnh lệnh đồng bộ nhưng không thể chặn lỗi hiển thị JSX. Error Boundary được thiết kế đặc biệt cho UI khai báo: nó chặn lỗi trong render, hook vòng đời và hàm tạo của component con, điều mà try-catch không thể làm do đặc thù của quá trình hiển thị React.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm