الگوهای رایج Data Fetching

در طول این فصل، از ساده‌ترین حالت (یک fetch داخل useEffect) شروع کردیم و لایه به لایه مشکلات واقعی را دیدیم: حالت‌های چندگانه، Race Condition، Abort، Caching، و در نهایت تفاوت بنیادین Server State با Client State. در این درس آخر فصل، چند الگوی رایج را که در عمل برای مدیریت بهتر این پیچیدگی به کار می‌روند، مرور می‌کنیم.

الگوی اول: استخراج به یک Custom Hook

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

function useFetch(url) {
  const [state, setState] = useState({ data: null, loading: true, error: null });

  useEffect(() => {
    const controller = new AbortController();
    setState({ data: null, loading: true, error: null });

    fetch(url, { signal: controller.signal })
      .then((res) => res.json())
      .then((data) => setState({ data, loading: false, error: null }))
      .catch((err) => {
        if (err.name !== "AbortError") {
          setState({ data: null, loading: false, error: err.message });
        }
      });

    return () => controller.abort();
  }, [url]);

  return state;
}

حالا هر کامپوننتی که به داده نیاز دارد، فقط یک خط می‌نویسد: const { data, loading, error } = useFetch(url)؛ بدون تکرار Abort، Race Condition یا مدیریت سه حالت.

الگوی دوم: Render-as-you-fetch در برابر Fetch-on-render

الگویی که در این فصل دیدیم (شروع درخواست داخل useEffect، پس از Render کامپوننت) به Fetch-on-render معروف است. یک مشکل این الگو این است که درخواست تا پس از Commit شدن کامپوننت شروع نمی‌شود؛ یعنی زمان گرانبهایی بین تصمیم به نمایش یک صفحه و شروع واقعی درخواست آن، هدر می‌رود. الگوی جایگزین، Render-as-you-fetch، درخواست را هم‌زمان با تصمیم به رندر کردن یک مسیر یا کامپوننت آغاز می‌کند، نه داخل خود کامپوننت. این الگو معمولاً به ابزار Routing یا Framework نیاز دارد و با Suspense، که در فصل بیست‌ویکم بررسی می‌شود، ارتباط نزدیکی دارد.

الگوی سوم: کتابخانه‌های تخصصی Server State

به‌خاطر حجم زیاد منطق تکراری‌ای که در این فصل دیدیم (Caching، Staleness، Deduplication درخواست‌های هم‌زمان)، اکوسیستم React چند کتابخانه تخصصی برای همین منظور ارائه داده که این مسائل را به‌طور کامل و آزموده‌شده حل می‌کنند؛ به‌جای اینکه هر تیم، نسخه دستی و ناقص خودش را بسازد. استفاده از این کتابخانه‌ها، معمولاً چیزی شبیه این به نظر می‌رسد:

// نمونه مفهومی، نه API دقیق یک کتابخانه خاص
function ProductPage({ id }) {
  const { data, isLoading, error } = useQuery(["product", id], () => fetchProduct(id));
  // Caching، Deduplication و Revalidation به‌طور خودکار مدیریت می‌شوند
}

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

الگوی چهارم: دریافت داده در سطح Framework

در برنامه‌هایی که با یک Framework مانند Next.js ساخته می‌شوند، بخشی از دریافت داده می‌تواند روی سرور، پیش از رسیدن به مرورگر کاربر، انجام شود؛ به‌جای اینکه کاربر منتظر یک درخواست از مرورگر خودش به سرور بماند. این رویکرد، با Server Components که در فصل بیست‌وچهارم بررسی می‌شود، ارتباط مستقیم دارد و بسیاری از مشکلاتی که در این فصل با useEffect دیدیم (مثل Loading State اولیه) را از ریشه کنار می‌گذارد.

کدام الگو را انتخاب کنیم

برای برنامه‌های کوچک یا برای یادگیری مدل ذهنی پایه، همان useEffect دستی (که در این فصل ساختیم) کاملاً قابل قبول است. برای برنامه‌های واقعی و بزرگ‌تر، معمولاً منطقی‌تر است از یک کتابخانه تخصصی Server State استفاده شود، نه بازسازی دستی همان قابلیت‌ها. انتخاب بین Framework-level fetching هم به معماری کلی پروژه بستگی دارد، نه فقط دریافت داده.

جمع‌بندی

استخراج منطق تکراری به یک Custom Hook، شروع زودتر درخواست با Render-as-you-fetch، استفاده از کتابخانه‌های تخصصی Server State، و دریافت داده در سطح Framework، چهار الگوی رایج برای مدیریت بهتر همان مسائلی هستند که در طول این فصل، قدم‌به‌قدم با useEffect دستی حل کردیم. با این درس، فصل چهاردهم به پایان می‌رسد و در ادامه به آزمون فصل ۱۴ می‌رویم.