استفاده از React Suspense در پروژه‌های React

React Suspense یکی از ویژگی‌هایی می‌باشد که در بین توسعه‌دهندگان React بسیار محبوب شده است. در این مقاله قصد داریم تا با مفهوم React Suspense بیشتر آشنا شویم و ویژگی‌ها، موارد استفاده و تغییراتی که استفاده از آن بر روی وب اپلیکیشن‌ها اعمال می‌کند را باهم بررسی نماییم.

React Suspense چیست؟

React suspense یکی از ویژگی‌هایی است که در نسخه ۱۶٫۶ React معرفی شده است. کامپوننت‌ها هنگامی که از این ویژگی استفاده می‌کنند، ممکن است در حالی که منتظر پایان یافتن یک فرآیند asynchronous، مانند بازیابی داده‌ها، هستند عمل رندر کردن را متوقف نمایند.

React suspense برای ساختن آسان‌تر برنامه‌هایی برای توسعه‌دهندگان ایجاد شد که نشانه‌های loading بهبود یافته و تجربه کاربری منسجم‌تری دارند. این ویژگی امکان متوقف کردن رندر درخت کامپوننت را تا زمانی که معیارهای خاصی برآورده شود، ممکن می‌سازد، که این اتفاق کار کردن توسعه‌دهندگان با داده‌های asynchronous را آسان‌تر می‌کند.

مشکلی که loading stateها در React با آن مواجه هستند

مدیریت loading stateها در React قبل از React Suspense کمی پیچیده‌تر بود. توسعه‌دهندگان مجبور بودند مکانیسم loading را با استفاده از فریم‌ورک‌های third-party مانند Redux یا Mobx، رندر شرطی یا مدیریت state پیاده‌سازی کنند. این کار اغلب منجر به ایجاد کدهای پیچیده و مستعد خطا می‌شد.

این مشکل توسط React Suspense حل شد که روش یکپارچه‌تری را برای مدیریت اقدامات asynchronous و loading stateها ارائه می‌دهد.

مفاهیم و ویژگی‌های کلیدی React Suspense

در این بخش قصد داریم تا در مورد برخی از مفاهیم و ویژگی‌ها صحبت کنیم تا به درک درستی از React Suspense و نحوه عملکرد آن برسیم.

کامپوننت Suspense

یک المنت ضروری React Suspense، کامپوننت Suspense است. کامپوننت Suspense این امکان را به ما می‌دهد تا نحوه مدیریت محتوای fallback را در حالی که اقدامات asynchronous در حال تعلیق هستند، بیان کنیم و هر بخشی از درخت کامپوننت خود را کپسوله نماییم.

<Suspense fallback={<LeoFallback />}>
  <LeoComponent />
</Suspense>

در مثالی که داریم، اگر LeoComponent آماده نباشد React به جای آن، کامپوننت LeoFallback را نمایش می‌دهد.

استفاده از React.lazy() یا lazy()

React دارای یک مکانیسم import داینامیک به نام lazy() است که به ما این امکان را می‌دهد تا کامپوننت‌ها را به شیوه lazy لود کنیم.

اساساً، lazy loading به این نیاز اشاره دارد که یک کامپوننت یا بخشی از کد فقط در صورت نیاز بارگذاری شود. این قابلیت، اغلب همراه با React Suspense استفاده می‌شود تا کامپوننت‌ها را فقط در صورت نیاز بارگذاری کند و به این ترتیب سرعت برنامه‌ای که داریم را افزایش دهد.

این کار برای به حداقل رساندن سرعت لود شدن برنامه و کاهش اندازه اولیه باندل بسیار مفید می‌باشد.

اکنون قصد داریم تا lazy() را عمیق‌تر بررسی کرده و نحوه عملکرد آن را یاد بگیریم.

سینتکس بیسیک lazy()

برای استفاده از lazy()، باید مراحل زیر را دنبال کنیم:

ابتدا کامپوننت‌های Suspense و همچنین هر کامپوننتی که می‌خواهیم با lazy بارگذاری کنیم را از React وارد می‌کنیم.

import { Suspense } from 'react';

سپس از lazy() برای تعریف import داینامیک استفاده می‌نماییم. در کامپوننتی که می‌خواهیم آن را به آرامی لود کنیم، lazy() یک تابع را به عنوان آرگومان می‌پذیرد و عبارت import داینامیک را تولید می‌کند.

const LeoComponent = lazy(() => import('./LeoComponent'));

در این مثال، LeoComponent در صورت نیاز به شکل lazy لود می‌شود. عبارت import() دینامیک هم مسیر کامپوننتی را که می‌خواهیم import کنیم، مشخص می‌نماید.

در مرحله بعد، المنتی را که می‌خواهیم به شکل lazy لود کنید، در یک المنت Suspense قرار می‌دهیم. می‌توانیم یک کامپوننت backup تعیین کنیم تا زمانی که کامپوننت لود شده به شکل lazy با استفاده از کامپوننت Suspense بازیابی می‌شود، نمایش داده شود.

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LeoComponent />
      </Suspense>
    </div>
  );
}

در مثال بالا، در حالی که LeoComponent در حال fetch شدن می‌باشد، کامپوننت fallback نمایش داده می‌شود که نشان می‌دهد محتوا در حال لود شدن است.

مزایای React.lazy()

  1. افزایش سرعت برنامه: با لود شدن انتخابی کامپوننت‌های مورد نیاز برای view فعلی و لود نکردن همه کامپوننت‌ها به طور همزمان، lazy loading کامپوننت‌ها می‌تواند سرعت برنامه را افزایش دهد.
  2. بهبود تجربه کاربری: با استفاده از Suspense به عنوان نشانه‌ای برای لود شدن داده‌های برنامه، می‌توانیم به کاربران اطلاع دهیم که برنامه در حال بارگذاری محتوا می‌باشد و به این ترتیب تجربه کاربری را بهبود ببخشیم.
  3. تقسیم کد: یکی از مزایای اصلی lazy() این است که تقسیم کد را امکان‌پذیر می‌کند. فرآیند تقسیم کد شامل تقسیم کد برنامه به بسته‌های کوچک‌تر و درخواستی است. این کار اندازه اولیه بسته نرم افزاری را به حداقل می‌رساند و زمان لود شدن برنامه را سریع‌تر می‌کند.

با استفاده از lazy()، می‌توانیم تقسیم کد و بارگذاری lazy کامپوننت‌ها را در برنامه‌های React خود انجام دهیم. این ویژگی ابزار مفیدی برای ساده‌سازی کارایی و کاهش زمان لود شدن اپلیکیشن‌ها می‌باشد، و با لود کردن کامپوننت‌ها تنها در صورت نیاز، تجربه کاربری را بهبود می‌بخشد.

Error Boundaryها

کامپوننت‌های React که به عنوان Error Boundaryها شناخته می‌شوند، توانایی شناسایی و مدیریت خطاهای داخل زیردرخت خود را دارند. از آنجا که آن‌ها می‌توانند هر مشکلی که در حین انتظار برای داده‌های asynchronous ایجاد می‌شود را مدیریت کنند، برای مقابله با suspense ضروری هستند.

class ErrorBoundary extends React.Component {
  componentDidCatch(error, info) {
    // Handle the error
  }

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

منظور از Rendering همزمان چیست؟

React Suspense به عنوان بخشی از Concurrent Mode معرفی شد که یک مجموعه آزمایشی از ویژگی‌های React می‌باشد. Rendering همزمان پس از آن معرفی شده است.

Rendering همزمان اجرای چندین کار را به صورت همزمان امکان‌پذیر می‌سازد و پاسخگویی و کارایی برنامه‌های React را بهبود می‌بخشد. این مورد یکی از کامپوننت‌های Concurrent Mode است، مجموعه‌ای از ویژگی‌های آزمون و خطا که برای غلبه بر برخی از معایب React rendering معمولی طراحی شده‌اند.

هدف اصلی rendering همزمان این است که از روان و پاسخگو بودن رابط کاربری حتی زمانی که React در حال مدیریت renderingهای پیچیده یا سایر عملیات asynchronous می‌باشد، اطمینان حاصل نماید.

React Suspense چگونه کار می‌کند؟

هنگام استفاده از React Suspense با عملیات asynchronous، مراحل زیر انجام می‌شود:

  1. پس از لود شدن React، درخت کامپوننت مجددا رندر می‌شود.
  2. React نگاه می‌کند تا ببیند که آیا هر یک از کامپوننت‌های child آن هنگام برخورد با کامپوننت Suspense در حالت تعلیق قرار می‌گیرند یا خیر.
  3. اگر یک کامپوننت child در انتظار داده باشد (به عنوان مثال، در نتیجه lazy() import یا دریافت داده)، React تا زمانی که داده‌ها آماده شوند fallback UI موجود را به کاربران نمایش می‌دهد.
  4. هنگامی که داده‌ها در دسترس قرار می‌گیرند، React به آرامی داده‌های واقعی را رندر کرده و آن‌ها به کاربران نشان می‌دهد.

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

بررسی موارد استفاده واقعی از React Suspense

React Suspense یک ابزار منعطف است که ممکن است در سناریوهای مختلفی مورد استفاده قرار بگیرد که شامل موارد زیر می‌باشد:

دریافت داده‌ها

ویژگی دریافت داده React Suspense مدیریت لود شدن asynchronous داده در برنامه‌های React را آسان‌تر می‌کند. React Suspense به ما این امکان را می‌دهد که rendering را تا زمانی که داده‌ها در دسترس قرار بگیرند به تعویق بیندازیم و با ارائه محتوای fallback یا نشانه‌هایی مبنی بر بارگذاری داده‌ها، تجربه کاربری را بهبود ببخشیم.

برای درک بهتر این موضوع، مثالی را با استفاده از یک API ساختگی برای نشان دادن نحوه بازیابی داده‌ها با استفاده از React Suspense بررسی می‌کنیم.

در ادامه روش استفاده از React Suspense برای مدیریت لود شدن داده‌ها، با فرض اینکه از فریم‌ورک‌هایی مانند React Query یا Relay برای دریافت داده‌ها استفاده می‌کنیم را داریم:

ایجاد Error Boundary و React Suspense

ابتدا باید یک Error Boundary و یک کامپوننت React Suspense تنظیم کنیم تا اگر حین بازیابی داده‌ها با هر گونه خطایی مواجه شدیم بتوانیم آن را ثبت نماییم. در مثال زیر نحوه ایجاد یک کامپوننت ErrorBoundary سفارشی در react برای دریافت داده را داریم:

import React from 'react';

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

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

  render() {
    if (this.state.hasError) {
      return <div>Error: Something is wrong!</div>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

استفاده از React Query برای دریافت داده‌ها

اگر از React Query برای بازیابی داده‌ها استفاده کنیم، می‌توانیم کامپوننتی بسازیم که از useQuery برای بازیابی داده‌ها استفاده می‌کند و آن را در Suspense قرار می‌دهد:

import React from 'react';
import { useQuery } from 'react-query';
import ErrorBoundary from './ErrorBoundary';

// Define your data-fetching function
async function fetchData() {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
}

function DataFetching() {
  const { data, isLoading, isError } = useQuery('data', fetchData);

  if (isLoading) {
    throw new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate loading delay
  }

  if (isError) {
    throw new Error('Error while fetching data');
  }

  return (
    <div>
      <h1>Fetching data with React Suspense</h1>
      <p>{data}</p>
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>Leo's App</h1>
      <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
          <DataFetchingComponent />
        </React.Suspense>
      </ErrorBoundary>
    </div>
  );
}

export default App;

در این مثال، ما داده‌ها را با استفاده از هوک useQuery دریافت می‌کنیم. برای نمایش نشانگر loading، یک Promise جدیدی را می‌سازیم تا در صورتی که لود شدن داده‌ها با تاخیر همراه بود، بتوانیم آن تاخیر را اعمال نماییم (isLoading دارای مقدار true است). همچنین یک error نیز ایجاد می‌کنیم تا در صورت وجود مشکل، error boundary بتواند آن را مدیریت کند.

می‌توانیم با قرار دادن کامپوننت DataFetching در Suspense، یک کامپوننت پشتیبان برای نمایش به کاربران در حین لود شدن داده‌ها تهیه نماییم. در این مثال یک پیام ساده Loading... را داریم.

همچنین باید اطمینان حاصل کنیم که مدیریت خطا، نشانگر loading و تابع دریافت داده همگی بر اساس نیازهای data-fetching منحصربه‌فرد ما شخصی‌سازی شده‌ باشند.

این مثال نشان می‌دهد که چگونه React Suspense مدیریت state و دریافت داده‌ها را آسان‌تر می‌کند و منجر به ایجاد یک پایگاه کد سازمان‌یافته‌تر و قابل درک‌تر می‌شود.

Lazy Loading در React Suspense

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

می‌توانیم از React.lazy() همراه با React Suspense استفاده کنیم تا به راحتی lazy loading را در برنامه خود قرار دهیم.

در اینجا مراحل اجرای lazy loading با React suspense را بررسی می‌کنیم:

ابتدا React Suspense را از React وارد می‌کنیم:

import { Suspense } from 'react';

در مرحله بعد، یک کامپوننت lazy loaded در برنامه React خود می‌سازیم. برای ساخت کامپوننتی که به کندی لود می‌شود، از متد React.lazy() استفاده می‌کنیم. برای import کردن کامپوننت به صورت داینامیک، از یک arrow function استفاده می‌کنیم:

const LennyComponent = lazy(() => import('./LennyComponent'));

سپس کامپوننت خود و قسمتی که می‌خواهیم به کندی لود شود را درون Suspense قرار می‌دهیم. در حالی که کامپوننت lazy در حال لود شدن است، می‌توانیم یک نشانگر loading یا کامپوننت fallback را برای نمایش به کاربران تعیین نماییم.

function App() {
  return (
    <div>
      <h1>Leo's App</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <LennyComponent />
      </Suspense>
    </div>
  );
}

این کامپوننت را می‌توانیم مانند هر کامپوننت دیگری در برنامه React خود مورد استفاده قرار دهیم.

function LennyComponent() {
  return <div>This component is lazily loaded.</div>;
}

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

این راه‌اندازی، اندازه اولیه باندل را به حداقل می‌رساند و با لود کردن LazyComponent در صورت نیاز، سرعت لود شدن برنامه React ما را افزایش می‌دهد. در حالی که کامپوننت در حال بازیابی است، کاربران نشانگر loading را مشاهده خواهند کرد (در این مثال، Loading... می‌باشد). این کامپوننت لود شده و به راحتی در برنامه رندر می‌شود.

تجربه کاربری بهتر

React Suspense را می‌توانیم برای اطمینان از تجربه کاربری روان با نشان دادن نشانگرهای loading یا محتوای fallback در حین لود شدن داده‌ها مورد استفاده قرار دهیم. این امر زمان loading درک شده را برای افرادی که از برنامه React ما استفاده می‌کنند کاهش می‌دهد.

برای مثال، فرض کنید یک کامپوننتی داریم که داده‌ها را از یک API با استفاده از fetch دریافت می‌کند و ما قصد داریم در حین دریافت داده‌ها، اسپینر loading را نمایش دهیم. در ادامه یک مثال با استفاده از React Suspense را داریم.

import React, { Suspense } from 'react';

// A component that fetches data
const fetchApiData = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Data loaded!');
    }, ۲۰۰۰); // Simulating a 2-second delay for data fetching
  });
};

// A component that uses Suspense to handle asynchronous data fetching
const DataComponent = () => {
  const apidata = fetchApiData(); // This can be any async function, like an API call

  return <div>{apiData}</div>;
};

// Wrapping the component with Suspense
const App = () => {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <DataComponent />
    </Suspense>
  );
};

// A simple loading spinner component
const LoadingSpinner = () => {
  return <div>Loading...</div>;
};

در کد بالا:

متد fetchApiData توسط کامپوننت DataComponent برای دریافت داده استفاده می‌شود. باید به این نکته توجه داشته باشیم که اگرچه fetchApiData یک تابع ساده است که یک promise را return می‌کند، اما ممکن است در واقع یک فراخوانی API در یک اپلیکیشن واقعی باشد.

کامپوننت Suspense که به یگ آرگومان fallback نیاز دارد، کامپوننت App را کپسوله می‌کند. یکی از کامپوننت‌هایی که در حین اجرای عملیات asynchronous نمایش داده می‌شود، prop fallback است. در این مثال این کامپوننت LoadingSpinner می‌باشد.

React Suspense هنگام رندر شدن DataComponent از دریافت خودکار داده‌های asynchronous مراقبت می‌کند. اگر داده‌ها هنوز در دسترس نباشند، کامپوننت LoadingSpinner رندر می‌شود. پس از بازیابی اطلاعات، رابط کاربری به‌روزرسانی می‌گردد.

این متد نیاز به مدیریت دستی state loading را از بین می‌برد و یک تجربه کاربری یکپارچه‌تری را ایجاد می‌کند.

جمع‌بندی

React Suspense یک روش ساده‌تر و یکپارچه‌تری را برای مدیریت اقدامات asynchronous و loading stateها ارائه می‌دهد. می‌توانیم از آن برای طراحی برنامه‌هایی استفاده کنیم که زمان loading سریع‌تر، دریافت داده‌های کارآمدتر و تجربه‌های کاربری بهتر را با استفاده از Suspense، React.lazy() و error boundaryها ارائه می‌دهند.

دیدگاه‌ها:

افزودن دیدگاه جدید