Menu
flag Ar iconالعربيةdown icon

حدود الأخطاء في React: التقاط أخطاء العرض مع بديل

حدود الأخطاء مكوّن يلتقط الأخطاء المرمية أثناء عرض المكوّنات بداخله ويعرض بديلًا بدلًا من شاشة فارغة. تعلّم ما تلتقطه، وكيف تكتبها كصنف، وكيف تعيد ضبطها، ومكتبة react-error-boundary.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

حدود الأخطاء (error boundary) مكوّن React يلتقط الأخطاء المرمية أثناء عرض المكوّنات بداخله ويعرض واجهة بديلة بدلًا من انهيار التطبيق كله. يجب أن تكون مكوّن صنف يعرّف static getDerivedStateFromError، لأنه لا يوجد خطاف لذلك. تكتبها مرة واحدة وتغلّف بها أي جزء من الشجرة.

اضغط "Break the profile" لتجعل الابن يرمي خطأ أثناء العرض.

يُستبدل الملف الشخصي بالبديل، ويستمر باقي الصفحة في العمل. تُظهر وحدة التحكم الخطأ مرتين: تسجّل React كل خطأ ملتقط بـ console.error، ويسجّله componentDidCatch مرة أخرى. يمسح "Try again" السبب (broken) وخطأ الحدود، فيُعرض الملف الشخصي من جديد. احذف this.props.onReset(); من reset فيرمي Try again مباشرة عائدًا إلى البديل، لأن شيئًا لم يصلح السبب.

كيف يعمل الصنف

تابعان يجعلان الصنف حدود أخطاء:

  • يعمل static getDerivedStateFromError(error) أثناء العرض، مباشرة بعد أن يرمي ابن خطأ. ويُرجع تحديث الحالة الذي يحوّل الحدود إلى بديلها. ويجب أن يكون نقيًا: لا تسجيل، ولا طلبات.
  • يعمل componentDidCatch(error, info) بعد أن يصبح البديل على الشاشة. استخدمه للآثار الجانبية مثل إرسال الخطأ إلى خدمة تقارير. وتسرد info.componentStack المكوّن الذي رمى الخطأ والمكوّنات فوقه.
class ErrorBoundary extends Component {
    state = { error: null };

    static getDerivedStateFromError(error) {
        return { error }; // switch to the fallback
    }

    componentDidCatch(error, info) {
        reportError(error, info.componentStack); // your logging service
    }

    render() {
        if (this.state.error) return this.props.fallback;
        return this.props.children;
    }
}

لا يوجد مكافئ بالخطافات، لذا حتى التطبيق المكتوب بمكوّنات الدوال فقط يحتاج إلى هذا الصنف الواحد. تشرح صفحة مكوّنات الأصناف صيغة الأصناف إن كانت جديدة عليك.

ما تلتقطه الحدود وما لا تلتقطه

تلتقط الحدود الأخطاء التي تحدث بينما تعمل React على المكوّنات داخلها:

  • العرض (جسم مكوّن الدالة، أو render الخاص بالصنف)،
  • توابع دورة الحياة والمُنشئات في مكوّنات الأصناف الأبناء،
  • التأثيرات (useEffect وuseLayoutEffect) ودوال المرجع،
  • الوعد المرفوض الذي يُقرأ بـ use، وفي React 19 الخطأ الذي يرميه إجراء نموذج أو دالة تُمرَّر إلى startTransition المأخوذة من useTransition.

وهي لا تلتقط:

  • الأخطاء في معالجات الأحداث،
  • الكود غير المتزامن الذي تبدؤه بنفسك: setTimeout ودوال .then() وawait في معالج حدث،
  • الأخطاء في render الخاص بالحدود نفسها (تلتقطها الحدود الأعلى التالية)،
  • الأخطاء أثناء العرض على الخادم.

تُستثنى معالجات الأحداث لأنها لا تكسر العرض. ما زالت الشاشة في حالة صالحة، فيكفي try/catch في المعالج. وإذا أردت فعلًا أن تظهر الحدود، فخزّن الخطأ في الحالة وارمِه في العرض التالي:

يترك الزر الأول الأزرار على الشاشة ولا يطبع إلا خطأ غير ملتقط في وحدة التحكم. أما الثاني فيلتقط الرفض، ويخزّنه، فتتولى الحدود الأمر.

أين تضع الحدود

حدود واحدة حول التطبيق كله توقف الشاشة البيضاء، لكن أي خطأ صغير عندها يستبدل كل شيء. ضع حدودًا حول الأجزاء التي يمكن أن تفشل باستقلال: عنصر في الشريط الجانبي، أو رسم بياني، أو كل مسار. عندها يكلّف الفشل المستخدم لوحة واحدة، لا الصفحة.

لا تعرض البديل إلا لوحة Stocks. انقل العنصرين إلى <ErrorBoundary> واحدة فتختفي لوحة Weather العاملة معها.

ومن دون أي حدود، تزيل React الشجرة كلها من الشاشة عندما لا يُلتقط خطأ عرض. الصفحة الفارغة أكثر أمانًا من صفحة مكسورة تعرض بيانات خاطئة، لكن المستخدمين يرونها كانهيار.

مكتبة react-error-boundary

تغلّف الحزمة react-error-boundary الصنف نيابة عنك وتضيف أدوات مساعدة لإعادة الضبط. وهي غير متاحة في هذا المحرر، فإليك بها في كتلة ثابتة:

import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';

function Fallback({ error, resetErrorBoundary }) {
    return (
        <div role="alert">
            <p>Something went wrong: {error.message}</p>
            <button onClick={resetErrorBoundary}>Try again</button>
        </div>
    );
}

<ErrorBoundary FallbackComponent={Fallback} onReset={() => refetch()} resetKeys={[userId]}>
    <Profile userId={userId} />
</ErrorBoundary>;

// Inside a child: hand an async error to the boundary
const { showBoundary } = useErrorBoundary();
fetchData().catch(showBoundary);

تعيد resetKeys ضبط الحدود عندما تتغير أي قيمة مسرودة، وتؤدي showBoundary حيلة «خزّن وارمِ» من المثال السابق.

الإبلاغ عن الأخطاء في React 19

تبلّغ React 19 عن الأخطاء بطريقة جديدة. الأخطاء التي تلتقطها الحدود تُسجَّل مرة واحدة بـ console.error، والأخطاء غير الملتقطة تذهب إلى window.reportError. ولإرسالها إلى خدمتك الخاصة، مرّر خيارات إلى createRoot. كل خيار يستبدل التسجيل الافتراضي المقابل:

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'), {
    onCaughtError(error, errorInfo) {
        // an error boundary caught it
        logToService('caught', error, errorInfo.componentStack);
    },
    onUncaughtError(error, errorInfo) {
        // no boundary caught it, and React removed the tree
        logToService('uncaught', error, errorInfo.componentStack);
    },
    onRecoverableError(error, errorInfo) {
        // React recovered by itself, for example after a hydration mismatch
        logToService('recovered', error, errorInfo.componentStack);
    },
});
root.render(<App />);

onRecoverableError للأخطاء التي أصلحتها React دون مساعدتك، مثل عدم تطابق في الإماهة أو خطأ عرض زال عندما أعادت React المحاولة. ويأخذ hydrateRoot الخيارات الثلاثة نفسها.

أخطاء شائعة

وضع الحدود داخل المكوّن الذي يرمي الخطأ. لا تلتقط الحدود إلا الأخطاء من أبنائها. إذا عرض Profile المكوّن <ErrorBoundary> حول JSX الخاصة به ثم رمى خطأ في جسمه، يتجاوزها الخطأ إلى الحدود الأعلى التالية. غلّف المكوّن من أبيه.

آثار جانبية في getDerivedStateFromError. يعمل أثناء العرض وقد يعمل أكثر من مرة، فاقصره على إرجاع الحالة. مكان التسجيل هو componentDidCatch أو onCaughtError على الجذر.

إعادة ضبط لا تصلح شيئًا. مسح خطأ الحدود يعرض الأبناء أنفسهم بـ props نفسها. إذا رموا خطأ من جديد، يعود المستخدم إلى البديل. غيّر ما سبب الخطأ أولًا (أعد الجلب، أو امسح المدخل السيئ، أو غيّر key)، ثم امسح الخطأ، كما يفعل المثال الأول بـ onReset.

حدود واحدة للتطبيق كله، ولا شيء غيرها. تمنع الصفحة الفارغة لكنها تحوّل كل فشل صغير إلى خطأ يملأ الصفحة. أضف حدودًا حول الميزات، واحتفظ بالحدود العليا كملاذ أخير.

بدائل تخفي الخطأ عنك. الرسالة الودية مناسبة للمستخدمين، لكن تأكد أن الخطأ ما زال يصل إلى سجلاتك. تسجّل React 19 الأخطاء الملتقطة في وحدة التحكم افتراضيًا؛ وفي الإنتاج تلك الوحدة هي وحدة مستخدمك، فأرسل الأخطاء إلى خدمة من componentDidCatch أو onCaughtError.

تقترن حدود الأخطاء بشكل طبيعي مع <Suspense>: يتولى Suspense «ليس جاهزًا بعد»، وتتولى الحدود «فشل». ضع الحدود خارج Suspense، كما في أمثلة use، ليغطي مكوّن واحد التحميل والنجاح والفشل.

الأسئلة الشائعة

ما هي حدود الأخطاء في React؟

مكوّن يلتقط أخطاء JavaScript المرمية أثناء عرض أي مكوّن بداخله، ويعرض واجهة بديلة بدلًا من ذلك. ومن دونها، يزيل خطأ أثناء العرض التطبيق كله من الشاشة.

هل يوجد خطاف لحدود الأخطاء؟

لا. يجب أن تكون حدود الأخطاء مكوّن صنف فيه static getDerivedStateFromError (واختياريًا componentDidCatch). تكتبها مرة واحدة وتستخدمها من مكوّنات الدوال مثل أي مكوّن آخر، أو تثبّت react-error-boundary، التي تغلّف الصنف نيابة عنك.

هل تلتقط حدود الأخطاء الأخطاء في معالجات الأحداث؟

لا. تعمل معالجات الأحداث خارج العرض، فلا تحتاج React إلى حدود لتتعافى. استخدم try/catch في المعالج، وإذا أردت أن تظهر الحدود، فخزّن الخطأ في الحالة وارمِه أثناء العرض.

هل تلتقط حدود الأخطاء الأخطاء غير المتزامنة؟

ليس من setTimeout أو دوال استدعاء الوعود أو fetch().then() التي تبدؤها بنفسك. لكنها تلتقط وعدًا مرفوضًا يُقرأ بـ use، وفي React 19 الخطأ المرمي داخل إجراء نموذج أو داخل دالة تُمرَّر إلى startTransition المأخوذة من useTransition.

كيف أعيد ضبط حدود الأخطاء؟

امسح حالة الخطأ فيها، عادة من زر Try again في البديل، بعد إصلاح ما سبب الخطأ. وتغيير key الخاص بالحدود يعيد ضبطها أيضًا، لأن React تركّب نسخة جديدة.

رسم توضيحي للغات البرمجة في Coddy

تعلّم البرمجة مع Coddy

ابدأ الآن