فارسی

با تسلط بر مرزهای خطای React، برنامه‌هایی مقاوم و کاربرپسند بسازید. بهترین شیوه‌ها، تکنیک‌های پیاده‌سازی و استراتژی‌های پیشرفته مدیریت خطا را بیاموزید.

مرزهای خطای React: تکنیک‌های مدیریت خطای زیبا برای برنامه‌های قوی

در دنیای پویای توسعه وب، ساخت برنامه‌های قوی و کاربرپسند از اهمیت بالایی برخوردار است. ری‌اکت، کتابخانه محبوب جاوا اسکریپت برای ساخت رابط‌های کاربری، مکانیزم قدرتمندی برای مدیریت خطاها به شیوه‌ای زیبا فراهم می‌کند: مرزهای خطا (Error Boundaries). این راهنمای جامع به مفهوم مرزهای خطا می‌پردازد و هدف، پیاده‌سازی و بهترین شیوه‌های ساخت برنامه‌های ری‌اکت مقاوم را بررسی می‌کند.

درک نیاز به مرزهای خطا

کامپوننت‌های ری‌اکت، مانند هر کدی، مستعد خطا هستند. این خطاها می‌توانند از منابع مختلفی ناشی شوند، از جمله:

بدون مدیریت خطای مناسب، یک خطا در یک کامپوننت ری‌اکت می‌تواند کل برنامه را از کار بیندازد و منجر به تجربه کاربری ضعیفی شود. مرزهای خطا راهی برای گرفتن این خطاها و جلوگیری از انتشار آن‌ها در درخت کامپوننت فراهم می‌کنند و تضمین می‌کنند که برنامه حتی در صورت شکست کامپوننت‌های جداگانه، عملکرد خود را حفظ کند.

مرزهای خطای React چه هستند؟

مرزهای خطا کامپوننت‌های ری‌اکت هستند که خطاهای جاوا اسکریپت را در هر کجای درخت کامپوننت فرزند خود می‌گیرند، آن خطاها را ثبت می‌کنند و به جای درخت کامپوننتی که از کار افتاده است، یک UI جایگزین (fallback) نمایش می‌دهند. آن‌ها مانند یک شبکه ایمنی عمل می‌کنند و از کرش کردن کل برنامه توسط خطاها جلوگیری می‌کنند.

ویژگی‌های کلیدی مرزهای خطا:

پیاده‌سازی مرزهای خطا

بیایید فرآیند ایجاد یک کامپوننت مرز خطای پایه را مرور کنیم:

۱. ایجاد کامپوننت مرز خطا

ابتدا، یک کامپوننت کلاسی جدید، به عنوان مثال به نام ErrorBoundary ایجاد کنید:


import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    // وضعیت را به‌روزرسانی کنید تا رندر بعدی UI جایگزین را نشان دهد.
    return {
      hasError: true
    };
  }

  componentDidCatch(error, errorInfo) {
    // همچنین می‌توانید خطا را در یک سرویس گزارش‌دهی خطا ثبت کنید
    console.error("Caught error: ", error, errorInfo);
    // مثال: logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // می‌توانید هر UI جایگزین سفارشی را رندر کنید
      return (
        <div>
          <h2>Something went wrong.</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            {this.state.error && this.state.error.toString()}
            <br />
            {this.state.errorInfo.componentStack}
          </details>
        </div>
      );
    }

    return this.props.children; 
  }
}

export default ErrorBoundary;

توضیح:

۲. استفاده از مرز خطا

برای استفاده از مرز خطا، کافی است هر کامپوننتی را که ممکن است خطا ایجاد کند با کامپوننت ErrorBoundary بپوشانید:


import ErrorBoundary from './ErrorBoundary';

function MyComponent() {
  // این کامپوننت ممکن است خطا ایجاد کند
  return (
    <ErrorBoundary>
      <PotentiallyBreakingComponent />
    </ErrorBoundary>
  );
}

export default MyComponent;

اگر PotentiallyBreakingComponent خطایی ایجاد کند، ErrorBoundary آن را گرفته، خطا را ثبت کرده و UI جایگزین را رندر می‌کند.

۳. مثال‌های گویا با زمینه جهانی

یک برنامه تجارت الکترونیک را در نظر بگیرید که اطلاعات محصول را از یک سرور راه دور دریافت و نمایش می‌دهد. یک کامپوننت به نام ProductDisplay مسئول رندر جزئیات محصول است. با این حال، سرور ممکن است گاهی اوقات داده‌های غیرمنتظره‌ای را برگرداند که منجر به خطاهای رندرینگ می‌شود.


// ProductDisplay.js
import React from 'react';

function ProductDisplay({ product }) {
  // شبیه‌سازی یک خطای احتمالی اگر product.price یک عدد نباشد
  if (typeof product.price !== 'number') {
    throw new Error('Invalid product price');
  }

  return (
    <div>
      <h2>{product.name}</h2>
      <p>Price: {product.price}</p>
      <img src={product.imageUrl} alt={product.name} />
    </div>
  );
}

export default ProductDisplay;

برای محافظت در برابر چنین خطاهایی، کامپوننت ProductDisplay را با یک ErrorBoundary بپوشانید:


// App.js
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import ProductDisplay from './ProductDisplay';

function App() {
  const product = {
    name: 'Example Product',
    price: 'Not a Number', // داده‌های عمداً نادرست
    imageUrl: 'https://example.com/image.jpg'
  };

  return (
    <div>
      <ErrorBoundary>
        <ProductDisplay product={product} />
      </ErrorBoundary>
    </div>
  );
}

export default App;

در این سناریو، از آنجایی که product.price عمداً به جای عدد به صورت رشته تنظیم شده است، کامپوننت ProductDisplay یک خطا ایجاد می‌کند. ErrorBoundary این خطا را می‌گیرد، از کرش کردن کل برنامه جلوگیری می‌کند و به جای کامپوننت خراب ProductDisplay، UI جایگزین را نمایش می‌دهد.

۴. مرزهای خطا در برنامه‌های بین‌المللی شده

هنگام ساخت برنامه‌ها برای مخاطبان جهانی، پیام‌های خطا باید برای ارائه تجربه کاربری بهتر، بومی‌سازی شوند. مرزهای خطا می‌توانند در کنار کتابخانه‌های بین‌المللی‌سازی (i18n) برای نمایش پیام‌های خطای ترجمه شده استفاده شوند.


// ErrorBoundary.js (با پشتیبانی از i18n)
import React from 'react';
import { useTranslation } from 'react-i18next'; // با فرض اینکه از react-i18next استفاده می‌کنید

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

  static getDerivedStateFromError(error) {
    return {
      hasError: true,
      error: error,
    };
  }

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
    this.setState({errorInfo: errorInfo});
  }

  render() {
    if (this.state.hasError) {
      return (
        <FallbackUI error={this.state.error} errorInfo={this.state.errorInfo}/>
      );
    }

    return this.props.children;
  }
}

const FallbackUI = ({error, errorInfo}) => {
  const { t } = useTranslation();

  return (
    <div>
      <h2>{t('error.title')}</h2>
      <p>{t('error.message')}</p>
      <details style={{ whiteSpace: 'pre-wrap' }}>
        {error && error.toString()}<br />
        {errorInfo?.componentStack}
      </details>
    </div>
  );
}


export default ErrorBoundary;

در این مثال، ما از react-i18next برای ترجمه عنوان و پیام خطا در UI جایگزین استفاده می‌کنیم. توابع t('error.title') و t('error.message') ترجمه‌های مناسب را بر اساس زبان انتخاب شده توسط کاربر بازیابی می‌کنند.

۵. ملاحظات برای رندر سمت سرور (SSR)

هنگام استفاده از مرزهای خطا در برنامه‌های رندر شده در سمت سرور، مدیریت مناسب خطاها برای جلوگیری از کرش کردن سرور بسیار مهم است. مستندات ری‌اکت توصیه می‌کند که از مرزهای خطا برای بازیابی از خطاهای رندرینگ در سرور استفاده نکنید. به جای آن، خطاها را قبل از رندر کامپوننت مدیریت کنید، یا یک صفحه خطای استاتیک را در سرور رندر کنید.

بهترین شیوه‌ها برای استفاده از مرزهای خطا

استراتژی‌های پیشرفته مدیریت خطا

۱. مکانیزم‌های تلاش مجدد

در برخی موارد، ممکن است با تلاش مجدد برای عملیاتی که باعث خطا شده است، بتوان از خطا بازیابی کرد. به عنوان مثال، اگر یک درخواست شبکه با شکست مواجه شود، می‌توانید پس از یک تأخیر کوتاه دوباره آن را امتحان کنید. مرزهای خطا می‌توانند با مکانیزم‌های تلاش مجدد ترکیب شوند تا تجربه کاربری مقاوم‌تری ارائه دهند.


// ErrorBoundaryWithRetry.js
import React from 'react';

class ErrorBoundaryWithRetry extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      retryCount: 0,
    };
  }

  static getDerivedStateFromError(error) {
    return {
      hasError: true,
    };
  }

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
  }

  handleRetry = () => {
    this.setState(prevState => ({
      hasError: false,
      retryCount: prevState.retryCount + 1,
    }), () => {
      // این کامپوننت را مجبور به رندر مجدد می‌کند. الگوهای بهتری را با propsهای کنترل شده در نظر بگیرید.
      this.forceUpdate(); // هشدار: با احتیاط استفاده کنید
      if (this.props.onRetry) {
          this.props.onRetry();
      }
    });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Something went wrong.</h2>
          <button onClick={this.handleRetry}>Retry</button>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundaryWithRetry;

کامپوننت ErrorBoundaryWithRetry شامل یک دکمه تلاش مجدد است که با کلیک بر روی آن، وضعیت hasError را بازنشانی کرده و کامپوننت‌های فرزند را دوباره رندر می‌کند. همچنین می‌توانید یک retryCount اضافه کنید تا تعداد تلاش‌های مجدد را محدود کنید. این رویکرد می‌تواند به ویژه برای مدیریت خطاهای گذرا، مانند قطعی‌های موقت شبکه، مفید باشد. اطمینان حاصل کنید که prop `onRetry` به درستی مدیریت می‌شود و منطقی که ممکن است خطا داده باشد را مجدداً دریافت/اجرا می‌کند.

۲. فلگ‌های ویژگی (Feature Flags)

فلگ‌های ویژگی به شما این امکان را می‌دهند که ویژگی‌ها را در برنامه خود به صورت پویا، بدون نیاز به استقرار کد جدید، فعال یا غیرفعال کنید. مرزهای خطا می‌توانند در کنار فلگ‌های ویژگی برای تخریب زیبای عملکرد در صورت بروز خطا استفاده شوند. به عنوان مثال، اگر یک ویژگی خاص باعث خطا می‌شود، می‌توانید آن را با استفاده از یک فلگ ویژگی غیرفعال کرده و پیامی به کاربر نمایش دهید که نشان می‌دهد این ویژگی به طور موقت در دسترس نیست.

۳. الگوی قطع‌کننده مدار (Circuit Breaker)

الگوی قطع‌کننده مدار یک الگوی طراحی نرم‌افزار است که برای جلوگیری از تلاش مکرر یک برنامه برای اجرای عملیاتی که احتمالاً با شکست مواجه می‌شود، استفاده می‌شود. این الگو با نظارت بر نرخ موفقیت و شکست یک عملیات کار می‌کند و اگر نرخ شکست از یک آستانه مشخص فراتر رود، "مدار را باز می‌کند" و از تلاش‌های بعدی برای اجرای آن عملیات برای یک دوره زمانی مشخص جلوگیری می‌کند. این می‌تواند به جلوگیری از شکست‌های زنجیره‌ای کمک کرده و پایداری کلی برنامه را بهبود بخشد.

مرزهای خطا می‌توانند برای پیاده‌سازی الگوی قطع‌کننده مدار در برنامه‌های ری‌اکت استفاده شوند. هنگامی که یک مرز خطا، خطایی را می‌گیرد، می‌تواند یک شمارنده شکست را افزایش دهد. اگر شمارنده شکست از یک آستانه فراتر رود، مرز خطا می‌تواند پیامی به کاربر نمایش دهد که نشان می‌دهد ویژگی به طور موقت در دسترس نیست و از تلاش‌های بعدی برای اجرای عملیات جلوگیری کند. پس از یک دوره زمانی مشخص، مرز خطا می‌تواند "مدار را ببندد" و اجازه دهد تلاش‌ها برای اجرای دوباره عملیات از سر گرفته شوند.

نتیجه‌گیری

مرزهای خطای ری‌اکت ابزاری ضروری برای ساخت برنامه‌های قوی و کاربرپسند هستند. با پیاده‌سازی مرزهای خطا، می‌توانید از کرش کردن کل برنامه توسط خطاها جلوگیری کنید، یک UI جایگزین زیبا به کاربران خود ارائه دهید و خطاها را برای اشکال‌زدایی و تجزیه و تحلیل در سرویس‌های نظارت ثبت کنید. با پیروی از بهترین شیوه‌ها و استراتژی‌های پیشرفته ذکر شده در این راهنما، می‌توانید برنامه‌های ری‌اکتی بسازید که مقاوم، قابل اعتماد و ارائه‌دهنده تجربه کاربری مثبت باشند، حتی در مواجهه با خطاهای غیرمنتظره. به یاد داشته باشید که بر ارائه پیام‌های خطای مفید که برای مخاطبان جهانی بومی‌سازی شده‌اند، تمرکز کنید.

مرزهای خطای React: تکنیک‌های مدیریت خطای زیبا برای برنامه‌های قوی | MLOG