ગુજરાતી

કોડ સ્પ્લિટિંગથી આગળ ડેટા ફેચિંગ માટે રિએક્ટ સસ્પેન્સનું અન્વેષણ કરો. Fetch-As-You-Render, એરર હેન્ડલિંગ અને વૈશ્વિક એપ્લિકેશન્સ માટે ભવિષ્ય-પ્રૂફ પેટર્ન્સ સમજો.

રિએક્ટ સસ્પેન્સ રિસોર્સ લોડિંગ: આધુનિક ડેટા ફેચિંગ પેટર્ન્સમાં નિપુણતા

વેબ ડેવલપમેન્ટની ગતિશીલ દુનિયામાં, યુઝર એક્સપિરિયન્સ (UX) સર્વોપરી છે. એપ્લિકેશન્સને ઝડપી, રિસ્પોન્સિવ અને આનંદદાયક હોવાની અપેક્ષા રાખવામાં આવે છે, ભલે નેટવર્કની સ્થિતિ કે ઉપકરણની ક્ષમતાઓ ગમે તેવી હોય. રિએક્ટ ડેવલપર્સ માટે, આ ઘણીવાર જટિલ સ્ટેટ મેનેજમેન્ટ, કોમ્પ્લેક્સ લોડિંગ ઇન્ડિકેટર્સ અને ડેટા ફેચિંગ વોટરફોલ્સ સામે સતત સંઘર્ષમાં પરિણમે છે. અહીં રિએક્ટ સસ્પેન્સ આવે છે, જે એક શક્તિશાળી, છતાં ઘણીવાર ગેરસમજ થયેલી સુવિધા છે, જે મૂળભૂત રીતે આપણે અસિંક્રોનસ ઓપરેશન્સ, ખાસ કરીને ડેટા ફેચિંગને કેવી રીતે હેન્ડલ કરીએ છીએ તેને બદલવા માટે બનાવવામાં આવી છે.

શરૂઆતમાં React.lazy() સાથે કોડ સ્પ્લિટિંગ માટે રજૂ કરાયેલ, સસ્પેન્સની સાચી સંભાવના તેની *કોઈપણ* અસિંક્રોનસ રિસોર્સના લોડિંગને ઓર્કેસ્ટ્રેટ કરવાની ક્ષમતામાં રહેલી છે, જેમાં API માંથી ડેટાનો પણ સમાવેશ થાય છે. આ વ્યાપક માર્ગદર્શિકા રિસોર્સ લોડિંગ માટે રિએક્ટ સસ્પેન્સમાં ઊંડાણપૂર્વક જશે, તેના મુખ્ય કોન્સેપ્ટ્સ, મૂળભૂત ડેટા ફેચિંગ પેટર્ન્સ અને પર્ફોર્મન્ટ અને સ્થિતિસ્થાપક વૈશ્વિક એપ્લિકેશન્સ બનાવવા માટેના વ્યવહારિક વિચારણાઓની શોધ કરશે.

રિએક્ટમાં ડેટા ફેચિંગનો વિકાસ: ઇમ્પેરેટિવથી ડિક્લેરેટિવ સુધી

ઘણા વર્ષોથી, રિએક્ટ કમ્પોનન્ટ્સમાં ડેટા ફેચિંગ મુખ્યત્વે એક સામાન્ય પેટર્ન પર આધારિત હતું: API કોલ શરૂ કરવા માટે useEffect હૂકનો ઉપયોગ કરવો, useState સાથે લોડિંગ અને એરર સ્ટેટ્સનું સંચાલન કરવું, અને આ સ્ટેટ્સના આધારે શરતી રીતે રેન્ડર કરવું. કાર્યકારી હોવા છતાં, આ અભિગમ ઘણીવાર કેટલાક પડકારો તરફ દોરી જાય છે:

સસ્પેન્સ વિના એક સામાન્ય ડેટા ફેચિંગ દૃશ્યનો વિચાર કરો:

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        setIsLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        setUser(data);
      } catch (e) {
        setError(e);
      } finally {
        setIsLoading(false);
      }
    };
    fetchUser();
  }, [userId]);

  if (isLoading) {
    return <p>Loading user profile...</p>;
  }

  if (error) {
    return <p style={"color: red;"}>Error: {error.message}</p>;
  }

  if (!user) {
    return <p>No user data available.</p>;
  }

  return (
    <div>
      <h2>User: {user.name}</h2>
      <p>Email: {user.email}</p>
      <!-- More user details -->
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>Welcome to the Application</h1>
      <UserProfile userId={"123"} />
    </div>
  );
}

આ પેટર્ન સર્વવ્યાપક છે, પરંતુ તે કમ્પોનન્ટને તેની પોતાની અસિંક્રોનસ સ્ટેટનું સંચાલન કરવા માટે દબાણ કરે છે, જે ઘણીવાર UI અને ડેટા ફેચિંગ લોજિક વચ્ચે ગાઢ રીતે જોડાયેલા સંબંધ તરફ દોરી જાય છે. સસ્પેન્સ એક વધુ ડિક્લેરેટિવ અને સુવ્યવસ્થિત વિકલ્પ પ્રદાન કરે છે.

કોડ સ્પ્લિટિંગથી આગળ રિએક્ટ સસ્પેન્સને સમજવું

મોટાભાગના ડેવલપર્સ પ્રથમ વખત સસ્પેન્સનો સામનો React.lazy() દ્વારા કોડ સ્પ્લિટિંગ માટે કરે છે, જ્યાં તે તમને કમ્પોનન્ટના કોડને તેની જરૂર ન પડે ત્યાં સુધી લોડ કરવાનું મુલતવી રાખવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે:

import React, { Suspense, lazy } from 'react';

const LazyComponent = lazy(() => import('./MyHeavyComponent'));

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

આ દૃશ્યમાં, જો MyHeavyComponent હજી સુધી લોડ ન થયું હોય, તો <Suspense> બાઉન્ડ્રી lazy() દ્વારા ફેંકવામાં આવેલ પ્રોમિસને પકડશે અને કમ્પોનન્ટનો કોડ તૈયાર ન થાય ત્યાં સુધી fallback પ્રદર્શિત કરશે. અહીં મુખ્ય સમજ એ છે કે સસ્પેન્સ રેન્ડરિંગ દરમિયાન ફેંકવામાં આવેલા પ્રોમિસને પકડીને કામ કરે છે.

આ મિકેનિઝમ ફક્ત કોડ લોડિંગ માટે જ નથી. રેન્ડરિંગ દરમિયાન કોલ કરાયેલું કોઈપણ ફંક્શન જે પ્રોમિસ ફેંકે છે (ઉદાહરણ તરીકે, કારણ કે રિસોર્સ હજી ઉપલબ્ધ નથી) તેને કમ્પોનન્ટ ટ્રીમાં ઉચ્ચ સ્તરે સસ્પેન્સ બાઉન્ડ્રી દ્વારા પકડી શકાય છે. જ્યારે પ્રોમિસ રીઝોલ્વ થાય છે, ત્યારે રિએક્ટ કમ્પોનન્ટને ફરીથી રેન્ડર કરવાનો પ્રયાસ કરે છે, અને જો રિસોર્સ હવે ઉપલબ્ધ હોય, તો ફોલબેક છુપાવવામાં આવે છે, અને વાસ્તવિક કન્ટેન્ટ પ્રદર્શિત થાય છે.

ડેટા ફેચિંગ માટે સસ્પેન્સના મુખ્ય કોન્સેપ્ટ્સ

ડેટા ફેચિંગ માટે સસ્પેન્સનો લાભ લેવા માટે, આપણે કેટલાક મુખ્ય સિદ્ધાંતો સમજવાની જરૂર છે:

1. પ્રોમિસ ફેંકવું

પ્રોમિસને રીઝોલ્વ કરવા માટે async/await નો ઉપયોગ કરતા પરંપરાગત અસિંક્રોનસ કોડથી વિપરીત, સસ્પેન્સ એક ફંક્શન પર આધાર રાખે છે જે જો ડેટા તૈયાર ન હોય તો પ્રોમિસ *ફેંકે* છે. જ્યારે રિએક્ટ આવા ફંક્શનને કોલ કરતા કમ્પોનન્ટને રેન્ડર કરવાનો પ્રયાસ કરે છે, અને ડેટા હજી પણ પેન્ડિંગ હોય, ત્યારે પ્રોમિસ ફેંકવામાં આવે છે. રિએક્ટ પછી તે કમ્પોનન્ટ અને તેના ચિલ્ડ્રનના રેન્ડરિંગને 'થોભાવે' છે, અને નજીકની <Suspense> બાઉન્ડ્રી શોધે છે.

2. સસ્પેન્સ બાઉન્ડ્રી

<Suspense> કમ્પોનન્ટ પ્રોમિસ માટે એરર બાઉન્ડ્રી તરીકે કામ કરે છે. તે એક fallback પ્રોપ લે છે, જે તેના કોઈપણ ચિલ્ડ્રન (અથવા તેમના વંશજો) સસ્પેન્ડ થતા હોય (એટલે ​​કે, પ્રોમિસ ફેંકતા હોય) ત્યારે રેન્ડર કરવા માટેનું UI છે. એકવાર તેની સબટ્રીમાં ફેંકવામાં આવેલા બધા પ્રોમિસ રીઝોલ્વ થઈ જાય, પછી ફોલબેકને વાસ્તવિક કન્ટેન્ટ દ્વારા બદલવામાં આવે છે.

એક જ સસ્પેન્સ બાઉન્ડ્રી બહુવિધ અસિંક્રોનસ ઓપરેશન્સનું સંચાલન કરી શકે છે. ઉદાહરણ તરીકે, જો તમારી પાસે સમાન <Suspense> બાઉન્ડ્રીમાં બે કમ્પોનન્ટ્સ હોય, અને દરેકને ડેટા ફેચ કરવાની જરૂર હોય, તો ફોલબેક *બંને* ડેટા ફેચ પૂર્ણ ન થાય ત્યાં સુધી પ્રદર્શિત થશે. આ આંશિક UI બતાવવાનું ટાળે છે અને વધુ સંકલિત લોડિંગ અનુભવ પ્રદાન કરે છે.

3. કેશ/રિસોર્સ મેનેજર (યુઝરલેન્ડની જવાબદારી)

ખાસ કરીને, સસ્પેન્સ પોતે ડેટા ફેચિંગ અથવા કેશિંગનું સંચાલન કરતું નથી. તે માત્ર એક સંકલન મિકેનિઝમ છે. ડેટા ફેચિંગ માટે સસ્પેન્સને કાર્યરત કરવા માટે, તમારે એક લેયરની જરૂર છે જે:

આ 'રિસોર્સ મેનેજર' સામાન્ય રીતે દરેક રિસોર્સની સ્થિતિ (પેન્ડિંગ, રીઝોલ્વ્ડ, અથવા એરર્ડ) સંગ્રહવા માટે એક સરળ કેશ (દા.ત., એક Map અથવા ઓબ્જેક્ટ) નો ઉપયોગ કરીને અમલમાં મુકાય છે. જ્યારે તમે પ્રદર્શન હેતુઓ માટે આ જાતે બનાવી શકો છો, વાસ્તવિક-દુનિયાની એપ્લિકેશનમાં, તમે એક મજબૂત ડેટા ફેચિંગ લાઇબ્રેરીનો ઉપયોગ કરશો જે સસ્પેન્સ સાથે સંકલિત થાય છે.

4. કોન્કરન્ટ મોડ (રિએક્ટ 18 ના સુધારા)

જ્યારે સસ્પેન્સનો ઉપયોગ રિએક્ટના જૂના સંસ્કરણોમાં થઈ શકે છે, તેની સંપૂર્ણ શક્તિ કોન્કરન્ટ રિએક્ટ (રિએક્ટ 18 માં createRoot સાથે ડિફોલ્ટ રૂપે સક્ષમ) સાથે અનલીશ થાય છે. કોન્કરન્ટ મોડ રિએક્ટને રેન્ડરિંગ કાર્યને વિક્ષેપિત કરવા, થોભાવવા અને ફરી શરૂ કરવાની મંજૂરી આપે છે. આનો અર્થ છે:

સસ્પેન્સ સાથે ડેટા ફેચિંગ પેટર્ન્સ

ચાલો સસ્પેન્સના આગમન સાથે ડેટા ફેચિંગ પેટર્ન્સના વિકાસનું અન્વેષણ કરીએ.

પેટર્ન 1: ફેચ-ધેન-રેન્ડર (સસ્પેન્સ રેપિંગ સાથે પરંપરાગત)

આ ક્લાસિક અભિગમ છે જ્યાં ડેટા ફેચ કરવામાં આવે છે, અને તે પછી જ કમ્પોનન્ટ રેન્ડર કરવામાં આવે છે. ડેટા માટે સીધા 'થ્રો પ્રોમિસ' મિકેનિઝમનો લાભ ન લેતા હોવા છતાં, તમે ફોલબેક પ્રદાન કરવા માટે સસ્પેન્સ બાઉન્ડ્રીમાં *આખરે* ડેટા રેન્ડર કરતા કમ્પોનન્ટને રેપ કરી શકો છો. આ સસ્પેન્સનો ઉપયોગ એવા કમ્પોનન્ટ્સ માટે જેનરિક લોડિંગ UI ઓર્કેસ્ટ્રેટર તરીકે વધુ છે જે આખરે તૈયાર થઈ જાય છે, ભલે તેમનું આંતરિક ડેટા ફેચિંગ હજી પણ પરંપરાગત useEffect આધારિત હોય.

import React, { Suspense, useState, useEffect } from 'react';

function UserDetails({ userId }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchUserData = async () => {
      setIsLoading(true);
      const res = await fetch(`/api/users/${userId}`);
      const data = await res.json();
      setUser(data);
      setIsLoading(false);
    };
    fetchUserData();
  }, [userId]);

  if (isLoading) {
    return <p>Loading user details...</p>;
  }

  return (
    <div>
      <h3>User: {user.name}</h3>
      <p>Email: {user.email}</p>
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>Fetch-Then-Render Example</h1>
      <Suspense fallback={<div>Overall page loading...</div>}>
        <UserDetails userId={"1"} />
      </Suspense>
    </div>
  );
}

ફાયદા: સમજવામાં સરળ, પાછળની સુસંગતતા. વૈશ્વિક લોડિંગ સ્ટેટ ઉમેરવા માટે ઝડપી માર્ગ તરીકે ઉપયોગ કરી શકાય છે.

ગેરફાયદા: UserDetails ની અંદર બોઇલરપ્લેટને દૂર કરતું નથી. જો કમ્પોનન્ટ્સ ક્રમિક રીતે ડેટા ફેચ કરે તો હજી પણ વોટરફોલ્સની સંભાવના છે. ડેટા માટે સસ્પેન્સના 'થ્રો-એન્ડ-કેચ' મિકેનિઝમનો સાચો લાભ લેતું નથી.

પેટર્ન 2: રેન્ડર-ધેન-ફેચ (રેન્ડરની અંદર ફેચિંગ, પ્રોડક્શન માટે નહીં)

આ પેટર્ન મુખ્યત્વે સસ્પેન્સ સાથે સીધું શું ન કરવું તે દર્શાવવા માટે છે, કારણ કે જો કાળજીપૂર્વક હેન્ડલ ન કરવામાં આવે તો તે અનંત લૂપ્સ અથવા પર્ફોર્મન્સ સમસ્યાઓ તરફ દોરી શકે છે. તેમાં યોગ્ય કેશિંગ મિકેનિઝમ *વિના*, કમ્પોનન્ટના રેન્ડર ફેઝમાં સીધા ડેટા ફેચ કરવાનો અથવા સસ્પેન્ડિંગ ફંક્શનને કોલ કરવાનો પ્રયાસ શામેલ છે.

// DO NOT USE THIS IN PRODUCTION WITHOUT A PROPER CACHING LAYER
// This is purely for illustration of how a direct 'throw' might work conceptually.

let fetchedData = null;
let dataPromise = null;

function fetchDataSynchronously(url) {
  if (fetchedData) {
    return fetchedData;
  }

  if (!dataPromise) {
    dataPromise = fetch(url)
      .then(res => res.json())
      .then(data => { fetchedData = data; dataPromise = null; return data; })
      .catch(err => { dataPromise = null; throw err; });
  }
  throw dataPromise; // This is where Suspense kicks in
}

function UserDetailsBadExample({ userId }) {
  const user = fetchDataSynchronously(`/api/users/${userId}`);
  return (
    <div>
      <h3>User: {user.name}</h3>
      <p>Email: {user.email}</p>
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>Render-Then-Fetch (Illustrative, NOT Recommended Directly)</h1>
      <Suspense fallback={<div>Loading user...</div>}>
        <UserDetailsBadExample userId={"2"} />
      </Suspense>
    </div>
  );
}

ફાયદા: બતાવે છે કે કેવી રીતે એક કમ્પોનન્ટ સીધા ડેટા માટે 'પૂછી' શકે છે અને જો તૈયાર ન હોય તો સસ્પેન્ડ થઈ શકે છે.

ગેરફાયદા: પ્રોડક્શન માટે અત્યંત સમસ્યારૂપ. આ મેન્યુઅલ, ગ્લોબલ fetchedData અને dataPromise સિસ્ટમ સરળ છે, બહુવિધ વિનંતીઓ, ઇનવેલિડેશન, અથવા એરર સ્ટેટ્સને મજબૂત રીતે હેન્ડલ કરતી નથી. તે 'થ્રો-અ-પ્રોમિસ' કોન્સેપ્ટનું એક આદિમ ઉદાહરણ છે, અપનાવવા માટેની પેટર્ન નથી.

પેટર્ન 3: ફેચ-એઝ-યુ-રેન્ડર (આદર્શ સસ્પેન્સ પેટર્ન)

આ તે પેરાડાઈમ શિફ્ટ છે જે સસ્પેન્સ ખરેખર ડેટા ફેચિંગ માટે સક્ષમ કરે છે. કમ્પોનન્ટને તેના ડેટાને ફેચ કરતા પહેલા રેન્ડર થવાની રાહ જોવાને બદલે, અથવા બધા ડેટાને અગાઉથી ફેચ કરવાને બદલે, ફેચ-એઝ-યુ-રેન્ડરનો અર્થ છે કે તમે ડેટા ફેચ કરવાનું *શક્ય તેટલું જલદી* શરૂ કરો છો, ઘણીવાર રેન્ડરિંગ પ્રક્રિયા *પહેલા* અથવા *સાથે સાથે*. કમ્પોનન્ટ્સ પછી કેશમાંથી ડેટા 'વાંચે' છે, અને જો ડેટા તૈયાર ન હોય, તો તે સસ્પેન્ડ થાય છે. મુખ્ય વિચાર ડેટા ફેચિંગ લોજિકને કમ્પોનન્ટના રેન્ડરિંગ લોજિકથી અલગ કરવાનો છે.

ફેચ-એઝ-યુ-રેન્ડરને અમલમાં મૂકવા માટે, તમારે એક મિકેનિઝમની જરૂર છે:

  1. કમ્પોનન્ટના રેન્ડર ફંક્શનની બહાર ડેટા ફેચ શરૂ કરવું (દા.ત., જ્યારે રૂટ દાખલ કરવામાં આવે, અથવા બટન ક્લિક કરવામાં આવે).
  2. પ્રોમિસ અથવા રીઝોલ્વ્ડ ડેટાને કેશમાં સંગ્રહિત કરવું.
  3. કમ્પોનન્ટ્સને આ કેશમાંથી 'વાંચવા' માટે એક માર્ગ પૂરો પાડવો. જો ડેટા હજી ઉપલબ્ધ ન હોય, તો રીડ ફંક્શન પેન્ડિંગ પ્રોમિસ ફેંકે છે.

આ પેટર્ન વોટરફોલ સમસ્યાને સંબોધિત કરે છે. જો બે અલગ-અલગ કમ્પોનન્ટ્સને ડેટાની જરૂર હોય, તો તેમની વિનંતીઓ સમાંતર રીતે શરૂ કરી શકાય છે, અને UI ફક્ત ત્યારે જ દેખાશે જ્યારે *બંને* તૈયાર હોય, જે એક જ સસ્પેન્સ બાઉન્ડ્રી દ્વારા સંચાલિત થાય છે.

મેન્યુઅલ અમલીકરણ (સમજવા માટે)

આંતરિક મિકેનિક્સને સમજવા માટે, ચાલો એક સરળ મેન્યુઅલ રિસોર્સ મેનેજર બનાવીએ. વાસ્તવિક એપ્લિકેશનમાં, તમે એક સમર્પિત લાઇબ્રેરીનો ઉપયોગ કરશો.

import React, { Suspense } from 'react';

// --- Simple Cache/Resource Manager --- //
const cache = new Map();

function createResource(promise) {
  let status = 'pending';
  let result;
  let suspender = promise.then(
    (r) => {
      status = 'success';
      result = r;
    },
    (e) => {
      status = 'error';
      result = e;
    }
  );

  return {
    read() {
      if (status === 'pending') {
        throw suspender;
      } else if (status === 'error') {
        throw result;
      } else if (status === 'success') {
        return result;
      }
    },
  };
}

function fetchData(key, fetcher) {
  if (!cache.has(key)) {
    cache.set(key, createResource(fetcher()));
  }
  return cache.get(key);
}

// --- Data Fetching Functions --- //
const fetchUserById = (id) => {
  console.log(`Fetching user ${id}...`);
  return new Promise(resolve => setTimeout(() => {
    const users = {
      '1': { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
      '2': { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
      '3': { id: '3', name: 'Charlie Brown', email: 'charlie@example.com' }
    };
    resolve(users[id]);
  }, 1500));
};

const fetchPostsByUserId = (userId) => {
  console.log(`Fetching posts for user ${userId}...`);
  return new Promise(resolve => setTimeout(() => {
    const posts = {
      '1': [{ id: 'p1', title: 'My First Post' }, { id: 'p2', title: 'Travel Adventures' }],
      '2': [{ id: 'p3', title: 'Coding Insights' }],
      '3': [{ id: 'p4', title: 'Global Trends' }, { id: 'p5', title: 'Local Cuisine' }]
    };
    resolve(posts[userId] || []);
  }, 2000));
};

// --- Components --- //
function UserProfile({ userId }) {
  const userResource = fetchData(`user-${userId}`, () => fetchUserById(userId));
  const user = userResource.read(); // This will suspend if user data is not ready

  return (
    <div>
      <h3>User: {user.name}</h3>
      <p>Email: {user.email}</p>
    </div>
  );
}

function UserPosts({ userId }) {
  const postsResource = fetchData(`posts-${userId}`, () => fetchPostsByUserId(userId));
  const posts = postsResource.read(); // This will suspend if posts data is not ready

  return (
    <div>
      <h4>Posts by {userId}:</h4>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
        {posts.length === 0 && <li>No posts found.</li>}
      </ul>
    </div>
  );
}

// --- Application --- //
let initialUserResource = null;
let initialPostsResource = null;

function prefetchDataForUser(userId) {
  initialUserResource = fetchData(`user-${userId}`, () => fetchUserById(userId));
  initialPostsResource = fetchData(`posts-${userId}`, () => fetchPostsByUserId(userId));
}

// Pre-fetch some data before the App component even renders
prefetchDataForUser('1');

function App() {
  return (
    <div>
      <h1>Fetch-As-You-Render with Suspense</h1>
      <p>This demonstrates how data fetching can happen in parallel, coordinated by Suspense.</p>

      <Suspense fallback={<div>Loading user profile and posts...</div>}>
        <UserProfile userId={"1"} />
        <UserPosts userId={"1"} />
      </Suspense>

      <h2>Another Section</h2>
      <Suspense fallback={<div>Loading different user...</div>}>
        <UserProfile userId={"2"} />
      </Suspense>
    </div>
  );
}

આ ઉદાહરણમાં:

ફેચ-એઝ-યુ-રેન્ડર માટે લાઇબ્રેરીઓ

મેન્યુઅલી એક મજબૂત રિસોર્સ મેનેજર બનાવવું અને જાળવવું જટિલ છે. સદભાગ્યે, ઘણી પરિપક્વ ડેટા ફેચિંગ લાઇબ્રેરીઓએ સસ્પેન્સ અપનાવ્યું છે અથવા અપનાવી રહી છે, જે યુદ્ધ-પરીક્ષિત ઉકેલો પૂરા પાડે છે:

આ લાઇબ્રેરીઓ રિસોર્સ બનાવવા અને સંચાલિત કરવાની, કેશિંગ, રિવેલિડેશન, ઓપ્ટિમિસ્ટિક અપડેટ્સ અને એરર હેન્ડલિંગની જટિલતાઓને દૂર કરે છે, જે ફેચ-એઝ-યુ-રેન્ડરને અમલમાં મૂકવાનું ઘણું સરળ બનાવે છે.

પેટર્ન 4: સસ્પેન્સ-અવેર લાઇબ્રેરીઓ સાથે પ્રીફેચિંગ

પ્રીફેચિંગ એક શક્તિશાળી ઓપ્ટિમાઇઝેશન છે જ્યાં તમે સક્રિયપણે એવો ડેટા ફેચ કરો છો જેની યુઝરને નજીકના ભવિષ્યમાં જરૂર પડવાની સંભાવના હોય, તે સ્પષ્ટપણે વિનંતી કરે તે પહેલાં જ. આ માનવામાં આવતા પર્ફોર્મન્સને નાટકીય રીતે સુધારી શકે છે.

સસ્પેન્સ-અવેર લાઇબ્રેરીઓ સાથે, પ્રીફેચિંગ સરળ બની જાય છે. તમે યુઝરની ક્રિયાપ્રતિક્રિયાઓ પર ડેટા ફેચ ટ્રિગર કરી શકો છો જે તરત જ UI ને બદલતી નથી, જેમ કે લિંક પર હોવર કરવું અથવા બટન પર માઉસ ફેરવવું.

import React, { Suspense } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';

// Assume these are your API calls
const fetchProductById = async (id) => {
  console.log(`Fetching product ${id}...`);
  return new Promise(resolve => setTimeout(() => {
    const products = {
      'A001': { id: 'A001', name: 'Global Widget X', price: 29.99, description: 'A versatile widget for international use.' },
      'B002': { id: 'B002', name: 'Universal Gadget Y', price: 149.99, description: 'Cutting-edge gadget, loved worldwide.' },
    };
    resolve(products[id]);
  }, 1000));
};

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      suspense: true, // Enable Suspense for all queries by default
    },
  },
});

function ProductDetails({ productId }) {
  const { data: product } = useQuery({
    queryKey: ['product', productId],
    queryFn: () => fetchProductById(productId),
  });

  return (
    <div style={{\"border\": \"1px solid #ccc\", \"padding\": \"15px\", \"margin\": \"10px 0\"}}>
      <h3>{product.name}</h3>
      <p>Price: ${product.price.toFixed(2)}</p>
      <p>{product.description}</p>
    </div>
  );
}

function ProductList() {
  const handleProductHover = (productId) => {
    // Prefetch data when a user hovers over a product link
    queryClient.prefetchQuery({
      queryKey: ['product', productId],
      queryFn: () => fetchProductById(productId),
    });
    console.log(`Prefetching product ${productId}`);
  };

  return (
    <div>
      <h2>Available Products:</h2>
      <ul>
        <li>
          <a href=\"#\" onMouseEnter={() => handleProductHover('A001')}
             onClick={(e) => { e.preventDefault(); /* Navigate or show details */ }}
          >Global Widget X (A001)</a>
        </li>
        <li>
          <a href=\"#\" onMouseEnter={() => handleProductHover('B002')}
             onClick={(e) => { e.preventDefault(); /* Navigate or show details */ }}
          >Universal Gadget Y (B002)</a>
        </li>
      </ul>
      <p>Hover over a product link to see prefetching in action. Open network tab to observe.</p>
    </div>
  );
}

function App() {
  const [showProductA, setShowProductA] = React.useState(false);
  const [showProductB, setShowProductB] = React.useState(false);

  return (
    <QueryClientProvider client={queryClient}>
      <h1>Prefetching with React Suspense (React Query)</h1>
      <ProductList />

      <button onClick={() => setShowProductA(true)}>Show Global Widget X</button>
      <button onClick={() => setShowProductB(true)}>Show Universal Gadget Y</button>

      {showProductA && (
        <Suspense fallback={<p>Loading Global Widget X...</p>}>
          <ProductDetails productId=\"A001\" />
        </Suspense>
      )}
      {showProductB && (
        <Suspense fallback={<p>Loading Universal Gadget Y...</p>}>
          <ProductDetails productId=\"B002\" />
        </Suspense>
      )}
    </QueryClientProvider>
  );
}

આ ઉદાહરણમાં, પ્રોડક્ટ લિંક પર હોવર કરવાથી `queryClient.prefetchQuery` ટ્રિગર થાય છે, જે બેકગ્રાઉન્ડમાં ડેટા ફેચ શરૂ કરે છે. જો યુઝર પછી પ્રોડક્ટની વિગતો બતાવવા માટે બટન ક્લિક કરે, અને ડેટા પ્રીફેચમાંથી પહેલેથી જ કેશમાં હોય, તો કમ્પોનન્ટ સસ્પેન્ડ થયા વિના તરત જ રેન્ડર થશે. જો પ્રીફેચ હજુ ચાલુ હોય અથવા શરૂ ન થયો હોય, તો સસ્પેન્સ ડેટા તૈયાર ન થાય ત્યાં સુધી ફોલબેક પ્રદર્શિત કરશે.

સસ્પેન્સ અને એરર બાઉન્ડ્રીઝ સાથે એરર હેન્ડલિંગ

જ્યારે સસ્પેન્સ ફોલબેક પ્રદર્શિત કરીને 'લોડિંગ' સ્ટેટને હેન્ડલ કરે છે, તે સીધી રીતે 'એરર' સ્ટેટ્સને હેન્ડલ કરતું નથી. જો સસ્પેન્ડિંગ કમ્પોનન્ટ દ્વારા ફેંકવામાં આવેલો પ્રોમિસ રિજેક્ટ થાય (એટલે ​​કે, ડેટા ફેચિંગ નિષ્ફળ જાય), તો આ એરર કમ્પોનન્ટ ટ્રીમાં ઉપર ફેલાશે. આ એરરને સુંદર રીતે હેન્ડલ કરવા અને યોગ્ય UI પ્રદર્શિત કરવા માટે, તમારે એરર બાઉન્ડ્રીઝનો ઉપયોગ કરવાની જરૂર છે.

એરર બાઉન્ડ્રી એ એક રિએક્ટ કમ્પોનન્ટ છે જે componentDidCatch અથવા static getDerivedStateFromError લાઇફસાઇકલ મેથડ્સનો અમલ કરે છે. તે તેના ચાઇલ્ડ કમ્પોનન્ટ ટ્રીમાં ગમે ત્યાં જાવાસ્ક્રિપ્ટ એરર્સને પકડે છે, જેમાં પ્રોમિસ દ્વારા ફેંકવામાં આવેલી એરરનો પણ સમાવેશ થાય છે જેને સસ્પેન્સ સામાન્ય રીતે જો તે પેન્ડિંગ હોત તો પકડત.

import React, { Suspense, useState } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';

// --- Error Boundary Component --- //
class MyErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    console.error("Caught an error:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (
        <div style={{\"border\": \"2px solid red\", \"padding\": \"20px\", \"margin\": \"20px 0\", \"background\": \"#ffe0e0\"}}>
          <h2>Something went wrong!</h2>
          <p>{this.state.error && this.state.error.message}</p>
          <p>Please try refreshing the page or contact support.</p>
          <button onClick={() => this.setState({ hasError: false, error: null })}>Try Again</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// --- Data Fetching (with potential for error) --- //
const fetchItemById = async (id) => {
  console.log(`Attempting to fetch item ${id}...`);
  return new Promise((resolve, reject) => setTimeout(() => {
    if (id === 'error-item') {
      reject(new Error('Failed to load item: Network unreachable or item not found.'));
    } else if (id === 'slow-item') {
      resolve({ id: 'slow-item', name: 'Delivered Slowly', data: 'This item took a while but arrived!', status: 'success' });
    } else {
      resolve({ id, name: `Item ${id}`, data: `Data for item ${id}` });
    }
  }, id === 'slow-item' ? 3000 : 800));
};

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      suspense: true,
      retry: false, // For demonstration, disable retry so error is immediate
    },
  },
});

function DisplayItem({ itemId }) {
  const { data: item } = useQuery({
    queryKey: ['item', itemId],
    queryFn: () => fetchItemById(itemId),
  });

  return (
    <div>
      <h3>Item Details:</h3>
      <p>ID: {item.id}</p>
      <p>Name: {item.name}</p>
      <p>Data: {item.data}</p>
    </div>
  );
}

function App() {
  const [fetchType, setFetchType] = useState('normal-item');

  return (
    <QueryClientProvider client={queryClient}>
      <h1>Suspense and Error Boundaries</h1>

      <div>
        <button onClick={() => setFetchType('normal-item')}>Fetch Normal Item</button>
        <button onClick={() => setFetchType('slow-item')}>Fetch Slow Item</button>
        <button onClick={() => setFetchType('error-item')}>Fetch Error Item</button>
      </div>

      <MyErrorBoundary>
        <Suspense fallback={<p>Loading item via Suspense...</p>}>
          <DisplayItem itemId={fetchType} />
        </Suspense>
      </MyErrorBoundary>
    </QueryClientProvider>
  );
}

તમારી સસ્પેન્સ બાઉન્ડ્રી (અથવા જે કમ્પોનન્ટ્સ સસ્પેન્ડ થઈ શકે છે)ને એરર બાઉન્ડ્રી સાથે રેપ કરીને, તમે સુનિશ્ચિત કરો છો કે ડેટા ફેચિંગ દરમિયાન નેટવર્ક નિષ્ફળતાઓ અથવા સર્વર એરર પકડાય અને સુંદર રીતે હેન્ડલ થાય, જે સમગ્ર એપ્લિકેશનને ક્રેશ થતા અટકાવે છે. આ એક મજબૂત અને યુઝર-ફ્રેન્ડલી અનુભવ પ્રદાન કરે છે, જે યુઝર્સને સમસ્યા સમજવા અને સંભવિતપણે ફરીથી પ્રયાસ કરવાની મંજૂરી આપે છે.

સસ્પેન્સ સાથે સ્ટેટ મેનેજમેન્ટ અને ડેટા ઇનવેલિડેશન

એ સ્પષ્ટ કરવું મહત્વપૂર્ણ છે કે રિએક્ટ સસ્પેન્સ મુખ્યત્વે અસિંક્રોનસ રિસોર્સની પ્રારંભિક લોડિંગ સ્ટેટને સંબોધિત કરે છે. તે સ્વાભાવિક રીતે ક્લાયન્ટ-સાઇડ કેશનું સંચાલન કરતું નથી, ડેટા ઇનવેલિડેશનને હેન્ડલ કરતું નથી, અથવા મ્યુટેશન્સ (create, update, delete operations) અને તેમના અનુગામી UI અપડેટ્સનું સંચાલન કરતું નથી.

આ તે સ્થાન છે જ્યાં સસ્પેન્સ-અવેર ડેટા ફેચિંગ લાઇબ્રેરીઓ (રિએક્ટ ક્વેરી, SWR, અપોલો ક્લાયન્ટ, રિલે) અનિવાર્ય બની જાય છે. તે સસ્પેન્સને પૂરક બનાવે છે:

એક મજબૂત ડેટા ફેચિંગ લાઇબ્રેરી વિના, મેન્યુઅલ સસ્પેન્સ રિસોર્સ મેનેજરની ટોચ પર આ સુવિધાઓને અમલમાં મૂકવી એ એક મહત્વપૂર્ણ કાર્ય હશે, જે અનિવાર્યપણે તમારે તમારું પોતાનું ડેટા ફેચિંગ ફ્રેમવર્ક બનાવવાની જરૂર પડશે.

વ્યવહારિક વિચારણાઓ અને શ્રેષ્ઠ પ્રયાસો

ડેટા ફેચિંગ માટે સસ્પેન્સ અપનાવવું એ એક મહત્વપૂર્ણ આર્કિટેક્ચરલ નિર્ણય છે. વૈશ્વિક એપ્લિકેશન માટે અહીં કેટલીક વ્યવહારિક વિચારણાઓ છે:

1. બધા ડેટાને સસ્પેન્સની જરૂર નથી

સસ્પેન્સ એવા નિર્ણાયક ડેટા માટે આદર્શ છે જે કમ્પોનન્ટના પ્રારંભિક રેન્ડરિંગને સીધી અસર કરે છે. બિન-નિર્ણાયક ડેટા, બેકગ્રાઉન્ડ ફેચ, અથવા મજબૂત દ્રશ્ય પ્રભાવ વિના આળસુ રીતે લોડ કરી શકાય તેવા ડેટા માટે, પરંપરાગત useEffect અથવા પ્રી-રેન્ડરિંગ હજી પણ યોગ્ય હોઈ શકે છે. સસ્પેન્સનો વધુ પડતો ઉપયોગ ઓછા દાણાદાર લોડિંગ અનુભવ તરફ દોરી શકે છે, કારણ કે એક જ સસ્પેન્સ બાઉન્ડ્રી તેના *બધા* ચિલ્ડ્રનને રીઝોલ્વ થવાની રાહ જુએ છે.

2. સસ્પેન્સ બાઉન્ડ્રીઝની ગ્રેન્યુલારિટી

તમારી <Suspense> બાઉન્ડ્રીઝને વિચારપૂર્વક મૂકો. તમારી એપ્લિકેશનના ટોચ પર એક જ, મોટી બાઉન્ડ્રી આખા પેજને સ્પિનરની પાછળ છુપાવી શકે છે, જે નિરાશાજનક હોઈ શકે છે. નાની, વધુ દાણાદાર બાઉન્ડ્રીઝ તમારા પેજના જુદા જુદા ભાગોને સ્વતંત્ર રીતે લોડ થવાની મંજૂરી આપે છે, જે વધુ પ્રગતિશીલ અને રિસ્પોન્સિવ અનુભવ પ્રદાન કરે છે. ઉદાહરણ તરીકે, યુઝર પ્રોફાઇલ કમ્પોનન્ટની આસપાસ એક બાઉન્ડ્રી, અને ભલામણ કરેલ પ્રોડક્ટ્સની સૂચિની આસપાસ બીજી બાઉન્ડ્રી.

<div>
  <h1>Product Page</h1>
  <Suspense fallback={<p>Loading main product details...</p>}>
    <ProductDetails id="prod123" />
  </Suspense>

  <hr />

  <h2>Related Products</h2>
  <Suspense fallback={<p>Loading related products...</p>}>
    <RelatedProducts category="electronics" />
  </Suspense>
</div>

આ અભિગમનો અર્થ એ છે કે યુઝર્સ મુખ્ય પ્રોડક્ટની વિગતો જોઈ શકે છે ભલે સંબંધિત પ્રોડક્ટ્સ હજી લોડ થઈ રહી હોય.

3. સર્વર-સાઇડ રેન્ડરિંગ (SSR) અને સ્ટ્રીમિંગ HTML

રિએક્ટ 18 ની નવી સ્ટ્રીમિંગ SSR API (renderToPipeableStream) સસ્પેન્સ સાથે સંપૂર્ણપણે સંકલિત છે. આ તમારા સર્વરને HTML મોકલવાની મંજૂરી આપે છે જલદી તે તૈયાર હોય, ભલે પેજના ભાગો (જેમ કે ડેટા-આધારિત કમ્પોનન્ટ્સ) હજી લોડ થઈ રહ્યા હોય. સર્વર એક પ્લેસહોલ્ડર (સસ્પેન્સ ફોલબેકમાંથી) સ્ટ્રીમ કરી શકે છે અને પછી જ્યારે ડેટા રીઝોલ્વ થાય ત્યારે વાસ્તવિક કન્ટેન્ટ સ્ટ્રીમ કરી શકે છે, સંપૂર્ણ ક્લાયન્ટ-સાઇડ રી-રેન્ડરની જરૂર વગર. આ વિવિધ નેટવર્ક પરિસ્થિતિઓ પર વૈશ્વિક યુઝર્સ માટે માનવામાં આવતા લોડિંગ પર્ફોર્મન્સને નોંધપાત્ર રીતે સુધારે છે.

4. વૃદ્ધિશીલ દત્તક

તમારે સસ્પેન્સનો ઉપયોગ કરવા માટે તમારી આખી એપ્લિકેશનને ફરીથી લખવાની જરૂર નથી. તમે તેને વૃદ્ધિશીલ રીતે રજૂ કરી શકો છો, નવી સુવિધાઓ અથવા કમ્પોનન્ટ્સથી શરૂ કરીને જે તેના ડિક્લેરેટિવ લોડિંગ પેટર્ન્સથી સૌથી વધુ લાભ મેળવશે.

5. ટૂલિંગ અને ડિબગિંગ

જ્યારે સસ્પેન્સ કમ્પોનન્ટ લોજિકને સરળ બનાવે છે, ડિબગિંગ અલગ હોઈ શકે છે. રિએક્ટ ડેવટૂલ્સ સસ્પેન્સ બાઉન્ડ્રીઝ અને તેમની સ્થિતિઓ વિશેની માહિતી પૂરી પાડે છે. તમારી પસંદ કરેલી ડેટા ફેચિંગ લાઇબ્રેરી તેની આંતરિક સ્થિતિને કેવી રીતે ખુલ્લી પાડે છે તેની સાથે પોતાને પરિચિત કરો (દા.ત., રિએક્ટ ક્વેરી ડેવટૂલ્સ).

6. સસ્પેન્સ ફોલબેક્સ માટે ટાઇમઆઉટ્સ

ખૂબ લાંબા લોડિંગ સમય માટે, તમે તમારા સસ્પેન્સ ફોલબેકમાં ટાઇમઆઉટ રજૂ કરવા માંગી શકો છો, અથવા ચોક્કસ વિલંબ પછી વધુ વિગતવાર લોડિંગ ઇન્ડિકેટર પર સ્વિચ કરી શકો છો. રિએક્ટ 18 માં useDeferredValue અને useTransition હુક્સ આ વધુ સૂક્ષ્મ લોડિંગ સ્ટેટ્સનું સંચાલન કરવામાં મદદ કરી શકે છે, જે તમને નવો ડેટા ફેચ થતો હોય ત્યારે UI નું 'જૂનું' સંસ્કરણ બતાવવાની મંજૂરી આપે છે, અથવા બિન-તાકીદના અપડેટ્સને મુલતવી રાખે છે.

રિએક્ટમાં ડેટા ફેચિંગનું ભવિષ્ય: રિએક્ટ સર્વર કમ્પોનન્ટ્સ અને તેનાથી આગળ

રિએક્ટમાં ડેટા ફેચિંગની યાત્રા ક્લાયન્ટ-સાઇડ સસ્પેન્સ સાથે અટકતી નથી. રિએક્ટ સર્વર કમ્પોનન્ટ્સ (RSC) એક મહત્વપૂર્ણ ઉત્ક્રાંતિનું પ્રતિનિધિત્વ કરે છે, જે ક્લાયન્ટ અને સર્વર વચ્ચેની રેખાઓને અસ્પષ્ટ કરવાનું અને ડેટા ફેચિંગને વધુ ઑપ્ટિમાઇઝ કરવાનું વચન આપે છે.

જેમ જેમ રિએક્ટ પરિપક્વ થતું રહેશે, તેમ તેમ સસ્પેન્સ અત્યંત પર્ફોર્મન્ટ, યુઝર-ફ્રેન્ડલી અને જાળવી શકાય તેવી એપ્લિકેશન્સ બનાવવા માટે પઝલનો એક વધુને વધુ કેન્દ્રીય ભાગ બનશે. તે ડેવલપર્સને અસિંક્રોનસ ઓપરેશન્સને હેન્ડલ કરવાની વધુ ડિક્લેરેટિવ અને સ્થિતિસ્થાપક રીત તરફ ધકેલે છે, જટિલતાને વ્યક્તિગત કમ્પોનન્ટ્સમાંથી એક સુવ્યવસ્થિત ડેટા લેયરમાં ખસેડે છે.

નિષ્કર્ષ

રિએક્ટ સસ્પેન્સ, શરૂઆતમાં કોડ સ્પ્લિટિંગ માટેની એક સુવિધા, ડેટા ફેચિંગ માટે એક પરિવર્તનકારી સાધન બની ગયું છે. Fetch-As-You-Render પેટર્નને અપનાવીને અને સસ્પેન્સ-અવેર લાઇબ્રેરીઓનો લાભ લઈને, ડેવલપર્સ તેમની એપ્લિકેશન્સના યુઝર એક્સપિરિયન્સને નોંધપાત્ર રીતે સુધારી શકે છે, લોડિંગ વોટરફોલ્સને દૂર કરી શકે છે, કમ્પોનન્ટ લોજિકને સરળ બનાવી શકે છે, અને સરળ, સંકલિત લોડિંગ સ્ટેટ્સ પ્રદાન કરી શકે છે. મજબૂત એરર હેન્ડલિંગ માટે એરર બાઉન્ડ્રીઝ અને રિએક્ટ સર્વર કમ્પોનન્ટ્સના ભવિષ્યના વચન સાથે મળીને, સસ્પેન્સ આપણને એવી એપ્લિકેશન્સ બનાવવાની શક્તિ આપે છે જે ફક્ત પર્ફોર્મન્ટ અને સ્થિતિસ્થાપક જ નથી, પણ વિશ્વભરના યુઝર્સ માટે સ્વાભાવિક રીતે વધુ આનંદદાયક પણ છે. સસ્પેન્સ-આધારિત ડેટા ફેચિંગ પેરાડાઈમમાં સ્થળાંતર માટે વૈચારિક ગોઠવણની જરૂર છે, પરંતુ કોડની સ્પષ્ટતા, પર્ફોર્મન્સ અને યુઝર સંતોષના સંદર્ભમાં લાભો નોંધપાત્ર છે અને રોકાણ માટે યોગ્ય છે.

રિએક્ટ સસ્પેન્સ રિસોર્સ લોડિંગ: આધુનિક ડેટા ફેચિંગ પેટર્ન્સમાં નિપુણતા | MLOG