فارسی

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

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

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

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

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

مرزهای خطا را مانند بلوک‌های `try...catch` برای کامپوننت‌های React در نظر بگیرید. با این حال، برخلاف `try...catch`، آنها برای کامپوننت‌ها کار می‌کنند و یک روش اعلانی (declarative) و قابل استفاده مجدد برای مدیریت خطاها در سراسر برنامه شما فراهم می‌کنند.

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

مرزهای خطا چندین مزیت حیاتی ارائه می‌دهند:

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

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

در اینجا یک مثال ساده از یک کامپوننت مرز خطا آورده شده است:


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

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

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

  render() {
    if (this.state.hasError) {
      // شما می‌توانید هر UI جایگزین سفارشی را رندر کنید
      return 

مشکلی پیش آمده است.

; } return this.props.children; } }

توضیح:

استفاده از مرزهای خطا

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



  


اگر ComponentThatMightThrow خطایی پرتاب کند، ErrorBoundary خطا را گرفته، state خود را به‌روزرسانی کرده و UI جایگزین خود را رندر می‌کند. بقیه برنامه به طور عادی به کار خود ادامه خواهند داد.

محل قرارگیری مرز خطا

محل قرارگیری مرزهای خطا برای مدیریت مؤثر خطا بسیار حیاتی است. این استراتژی‌ها را در نظر بگیرید:

مثال:


function App() {
  return (
    
); }

در این مثال، هر بخش اصلی برنامه (Header, Sidebar, ContentArea, Footer) با یک مرز خطا پوشانده شده است. این به هر بخش اجازه می‌دهد تا خطاها را به طور مستقل مدیریت کند و از تأثیر یک خطا بر کل برنامه جلوگیری می‌کند.

سفارشی‌سازی UI جایگزین

UI جایگزینی که توسط یک مرز خطا نمایش داده می‌شود باید آموزنده و کاربرپسند باشد. این دستورالعمل‌ها را در نظر بگیرید:

مثال:


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

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

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

  render() {
    if (this.state.hasError) {
      // شما می‌توانید هر UI جایگزین سفارشی را رندر کنید
      return (
        

وای! مشکلی پیش آمده است.

متأسفیم، اما در حین نمایش این محتوا خطایی رخ داد.

لطفاً صفحه را رفرش کنید یا در صورت ادامه مشکل با پشتیبانی تماس بگیرید.

تماس با پشتیبانی
); } return this.props.children; } }

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

مدیریت انواع مختلف خطاها

مرزهای خطا، خطاهایی را که در حین رندرینگ، در متدهای چرخه حیات و در سازنده‌های کل درخت زیرمجموعه خود رخ می‌دهند، می‌گیرند. آنها خطاهای مربوط به موارد زیر را *نمی‌گیرند*:

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

مدیریت‌کننده‌های رویداد

برای خطاهایی که در مدیریت‌کننده‌های رویداد رخ می‌دهند، از یک بلوک استاندارد try...catch استفاده کنید:


function MyComponent() {
  const handleClick = () => {
    try {
      // کدی که ممکن است خطا پرتاب کند
      throw new Error("Something went wrong in the event handler");
    } catch (error) {
      console.error("Error in event handler: ", error);
      // خطا را مدیریت کنید (مثلاً، نمایش پیام خطا)
      alert("خطایی رخ داد. لطفاً دوباره تلاش کنید.");
    }
  };

  return ;
}

کد ناهمگام

برای خطاهایی که در کد ناهمگام رخ می‌دهند، از بلوک‌های try...catch درون تابع ناهمگام استفاده کنید:


function MyComponent() {
  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch("https://api.example.com/data");
        const data = await response.json();
        // پردازش داده‌ها
        console.log(data);
      } catch (error) {
        console.error("Error fetching data: ", error);
        // خطا را مدیریت کنید (مثلاً، نمایش پیام خطا)
        alert("دریافت داده‌ها با شکست مواجه شد. لطفاً بعداً دوباره تلاش کنید.");
      }
    }

    fetchData();
  }, []);

  return 
در حال بارگذاری داده‌ها...
; }

به عنوان جایگزین، می‌توانید از یک مکانیزم مدیریت خطای سراسری برای promise rejectionهای مدیریت نشده استفاده کنید:


window.addEventListener('unhandledrejection', function(event) {
  console.error('Unhandled rejection (promise: ', event.promise, ', reason: ', event.reason, ');');
  // به صورت اختیاری یک پیام خطای سراسری نمایش دهید یا خطا را در یک سرویس ثبت کنید
  alert("یک خطای غیرمنتظره رخ داد. لطفاً بعداً دوباره تلاش کنید.");
});

تکنیک‌های پیشرفته مرز خطا

بازنشانی (Resetting) مرز خطا

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

برای بازنشانی یک مرز خطا، می‌توانید از یک کتابخانه مدیریت state مانند Redux یا Context برای مدیریت وضعیت خطا و ارائه یک تابع بازنشانی استفاده کنید. به عنوان جایگزین، می‌توانید از یک رویکرد ساده‌تر با وادار کردن مرز خطا به بازنصب (remount) استفاده کنید.

مثال (وادار کردن به بازنصب):


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

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

  componentDidCatch(error, info) {
    // شما همچنین می‌توانید خطا را در یک سرویس گزارش خطا ثبت کنید
    console.error("Caught an error: ", error, info.componentStack);
    this.setState(prevState => ({ errorCount: prevState.errorCount + 1 }));
  }

  resetError = () => {
      this.setState({hasError: false, key: this.state.key + 1})
  }

  render() {
    if (this.state.hasError) {
      // شما می‌توانید هر UI جایگزین سفارشی را رندر کنید
      return (
        

وای! مشکلی پیش آمده است.

متأسفیم، اما در حین نمایش این محتوا خطایی رخ داد.

); } return
{this.props.children}
; } }

در این مثال، یک 'key' به div بسته‌بندی‌کننده اضافه شده است. تغییر key باعث می‌شود که کامپوننت دوباره بازنصب (remount) شود و به طور مؤثر حالت خطا را پاک کند. متد `resetError` وضعیت `key` کامپوننت را به‌روزرسانی می‌کند و باعث می‌شود کامپوننت بازنصب شده و فرزندان خود را دوباره رندر کند.

استفاده از مرزهای خطا با Suspense

React Suspense به شما این امکان را می‌دهد که رندر یک کامپوننت را تا زمانی که شرطی برآورده شود (مثلاً، داده‌ها دریافت شوند) «معلق» کنید. شما می‌توانید مرزهای خطا را با Suspense ترکیب کنید تا تجربه مدیریت خطای قوی‌تری برای عملیات ناهمگام فراهم کنید.


import React, { Suspense } from 'react';

function MyComponent() {
  return (
    
      در حال بارگذاری...
}> ); } function DataFetchingComponent() { const data = useData(); // هوک سفارشی که داده‌ها را به صورت ناهمگام دریافت می‌کند return
{data.value}
; }

در این مثال، DataFetchingComponent داده‌ها را به صورت ناهمگام با استفاده از یک هوک سفارشی دریافت می‌کند. کامپوننت Suspense یک نشانگر بارگذاری را در حین دریافت داده‌ها نمایش می‌دهد. اگر در حین فرآیند دریافت داده‌ها خطایی رخ دهد، ErrorBoundary خطا را گرفته و یک UI جایگزین نمایش می‌دهد.

بهترین شیوه‌ها برای مرزهای خطای React

مثال‌های دنیای واقعی

در اینجا چند مثال واقعی از نحوه استفاده از مرزهای خطا آورده شده است:

جایگزین‌های مرزهای خطا

در حالی که مرزهای خطا روش پیشنهادی برای مدیریت خطا در React هستند، رویکردهای جایگزین دیگری نیز وجود دارد که می‌توانید در نظر بگیرید. با این حال، به خاطر داشته باشید که این جایگزین‌ها ممکن است به اندازه مرزهای خطا در جلوگیری از کرش کردن برنامه و ارائه یک تجربه کاربری یکپارچه مؤثر نباشند.

در نهایت، مرزهای خطا یک رویکرد قوی و استاندارد برای مدیریت خطا در React ارائه می‌دهند و آنها را به انتخاب ارجح برای اکثر موارد استفاده تبدیل می‌کنند.

نتیجه‌گیری

مرزهای خطای React ابزاری ضروری برای ساخت برنامه‌های React قوی و کاربرپسند هستند. با گرفتن خطاها و نمایش UIهای جایگزین، آنها از کرش کردن برنامه جلوگیری می‌کنند، تجربه کاربری را بهبود می‌بخشند و دیباگ کردن خطا را ساده‌تر می‌کنند. با پیروی از بهترین شیوه‌های ذکر شده در این راهنما، می‌توانید به طور مؤثر مرزهای خطا را در برنامه‌های خود پیاده‌سازی کرده و یک تجربه کاربری پایدارتر و قابل اعتمادتر برای کاربران در سراسر جهان ایجاد کنید.