<Suspense>
<Suspense> அதன் children loading முடியும் வரை fallback ஒன்றைக் காட்ட அனுமதிக்கிறது.
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>- குறிப்பு
- பயன்பாடு
- content loading ஆகும்போது fallback காட்டுதல்
- content-ஐ ஒரே நேரத்தில் சேர்த்து வெளிப்படுத்துதல்
- nested content load ஆகும் போதே அதை வெளிப்படுத்துதல்
- புதிய content loading ஆகும்போது பழைய content-ஐக் காட்டுதல்
- ஏற்கனவே வெளிப்பட்ட content மறைக்கப்படுவதைத் தடுக்குதல்
- Transition நடந்து கொண்டிருப்பதைச் சுட்டிக்காட்டுதல்
- navigation-இல் Suspense boundaries-ஐ reset செய்தல்
- server errors மற்றும் client-only content-க்கு fallback வழங்குதல்
- Troubleshooting
குறிப்பு
<Suspense>
Props
children: நீங்கள் render செய்ய நினைக்கும் உண்மையான UI. rendering நடக்கும் போதுchildrensuspend ஆனால், Suspense boundaryfallback-ஐ render செய்வதற்கு மாறும்.fallback: உண்மையான UI loading முடிக்கவில்லை என்றால், அதன் இடத்தில் render செய்யப்படும் மாற்று UI. எந்த செல்லுபடியான React node-ஐயும் ஏற்கலாம்; நடைமுறையில் fallback என்பது loading spinner அல்லது skeleton போன்ற இலகுவான placeholder view ஆக இருக்கும்.childrensuspend ஆகும்போது Suspense தானாகவேfallback-க்கு மாறி, data தயாரானதும் மீண்டும்children-க்கு திரும்பும். rendering நடக்கும் போதுfallbacksuspend ஆனால், அது அருகிலுள்ள parent Suspense boundary-ஐ இயக்கும்.
கவனிக்க வேண்டியவை
- முதல் முறையாக mount ஆகும் முன்பே suspend ஆன renders-க்காக React எந்த state-ஐயும் பாதுகாக்காது. component load ஆனதும், suspend ஆன tree-ஐ React ஆரம்பத்திலிருந்து மீண்டும் render செய்ய முயலும்.
- Suspense ஏற்கனவே tree-க்கான content-ஐக் காட்டிக் கொண்டிருந்தபோது அது மீண்டும் suspend ஆனால், அந்த update
startTransitionஅல்லதுuseDeferredValueமூலம் ஏற்பட்டதல்ல என்றால்fallbackமீண்டும் காட்டப்படும். - மீண்டும் suspend ஆனதால் ஏற்கனவே தெரியும் content-ஐ React மறைக்க வேண்டியிருந்தால், content tree-இல் உள்ள layout Effects-ஐ அது clean up செய்யும். content மீண்டும் காட்டத் தயாரானதும், React layout Effects-ஐ மீண்டும் fire செய்யும். இதனால் DOM layout-ஐ அளக்கும் Effects, content மறைக்கப்பட்டிருக்கும்போது அதைச் செய்ய முயலாது.
- React-இல் Suspense உடன் ஒருங்கிணைக்கப்பட்ட Streaming Server Rendering மற்றும் Selective Hydration போன்ற உள்ளக optimizations உள்ளன. மேலும் அறிய architecture overview-ஐப் படித்து, technical talk-ஐப் பாருங்கள்.
பயன்பாடு
content loading ஆகும்போது fallback காட்டுதல்
உங்கள் application-ன் எந்த பகுதியையும் Suspense boundary-யால் wrap செய்யலாம்:
<Suspense fallback={<Loading />}>
<Albums />
</Suspense>children-க்கு தேவையான code மற்றும் data அனைத்தும் load ஆகும் வரை, React உங்கள் loading fallback-ஐக் காட்டும்.
கீழே உள்ள எடுத்துக்காட்டில், albums பட்டியலை fetch செய்யும் போது Albums component suspend ஆகிறது. அது render ஆகத் தயாராகும் வரை, React மேலுள்ள அருகிலுள்ள Suspense boundary-யை fallback-ஆகிய உங்கள் Loading component-ஐக் காட்டுமாறு மாற்றும். பின்னர் data load ஆனதும், React Loading fallback-ஐ மறைத்து, data உடன் Albums component-ஐ render செய்கிறது.
import { Suspense } from 'react'; import Albums from './Albums.js'; export default function ArtistPage({ artist }) { return ( <> <h1>{artist.name}</h1> <Suspense fallback={<Loading />}> <Albums artistId={artist.id} /> </Suspense> </> ); } function Loading() { return <h2>🌀 ஏற்றுகிறது...</h2>; }
content-ஐ ஒரே நேரத்தில் சேர்த்து வெளிப்படுத்துதல்
இயல்பாக, Suspense-க்குள் உள்ள முழு tree-யும் ஒரே unit ஆக கருதப்படும். எடுத்துக்காட்டாக, இந்த components-இல் ஒன்று மட்டுமே data-க்காக காத்திருந்து suspend ஆனாலும், அவை அனைத்தும் சேர்ந்து loading indicator-ஆல் மாற்றப்படும்:
<Suspense fallback={<Loading />}>
<Biography />
<Panel>
<Albums />
</Panel>
</Suspense>பின்னர் அவை அனைத்தும் காட்டத் தயாரானதும், அனைத்தும் ஒரே நேரத்தில் சேர்ந்து தோன்றும்.
கீழே உள்ள எடுத்துக்காட்டில், Biography மற்றும் Albums இரண்டும் சில data-ஐ fetch செய்கின்றன. ஆனால் அவை ஒரே Suspense boundary-க்குள் தொகுக்கப்பட்டிருப்பதால், இந்த components எப்போதும் ஒரே நேரத்தில் சேர்ந்து “pop in” ஆகும்.
import { Suspense } from 'react'; import Albums from './Albums.js'; import Biography from './Biography.js'; import Panel from './Panel.js'; export default function ArtistPage({ artist }) { return ( <> <h1>{artist.name}</h1> <Suspense fallback={<Loading />}> <Biography artistId={artist.id} /> <Panel> <Albums artistId={artist.id} /> </Panel> </Suspense> </> ); } function Loading() { return <h2>🌀 ஏற்றுகிறது...</h2>; }
data load செய்யும் components, Suspense boundary-ன் நேரடி children ஆக இருக்க வேண்டிய அவசியமில்லை. எடுத்துக்காட்டாக, Biography மற்றும் Albums-ஐ புதிய Details component-க்குள் நகர்த்தலாம். இது behavior-ஐ மாற்றாது. Biography மற்றும் Albums ஒரே அருகிலுள்ள parent Suspense boundary-யைப் பகிர்வதால், அவற்றின் reveal சேர்த்து ஒருங்கிணைக்கப்படும்.
<Suspense fallback={<Loading />}>
<Details artistId={artist.id} />
</Suspense>
function Details({ artistId }) {
return (
<>
<Biography artistId={artistId} />
<Panel>
<Albums artistId={artistId} />
</Panel>
</>
);
}nested content load ஆகும் போதே அதை வெளிப்படுத்துதல்
ஒரு component suspend ஆகும்போது, அருகிலுள்ள parent Suspense component fallback-ஐக் காட்டும். இதனால் loading sequence ஒன்றை உருவாக்க பல Suspense components-ஐ nest செய்யலாம். அடுத்த நிலை content கிடைக்கத் தொடங்கும் போது, ஒவ்வொரு Suspense boundary-ன் fallback நிரப்பப்படும். எடுத்துக்காட்டாக, album பட்டியலுக்கே தனி fallback கொடுக்கலாம்:
<Suspense fallback={<BigSpinner />}>
<Biography />
<Suspense fallback={<AlbumsGlimmer />}>
<Panel>
<Albums />
</Panel>
</Suspense>
</Suspense>இந்த மாற்றத்துடன், Biography-ஐக் காட்டுவதற்கு Albums load ஆக “காத்திருக்க” வேண்டியதில்லை.
sequence இதுவாக இருக்கும்:
Biographyஇன்னும் load ஆகவில்லை என்றால், முழு content area-வின் இடத்தில்BigSpinnerகாட்டப்படும்.Biographyloading முடித்ததும்,BigSpinnercontent-ஆல் மாற்றப்படும்.Albumsஇன்னும் load ஆகவில்லை என்றால்,Albumsமற்றும் அதன் parentPanelஇடத்தில்AlbumsGlimmerகாட்டப்படும்.- இறுதியாக,
Albumsloading முடித்ததும், அதுAlbumsGlimmer-ஐ மாற்றும்.
import { Suspense } from 'react'; import Albums from './Albums.js'; import Biography from './Biography.js'; import Panel from './Panel.js'; export default function ArtistPage({ artist }) { return ( <> <h1>{artist.name}</h1> <Suspense fallback={<BigSpinner />}> <Biography artistId={artist.id} /> <Suspense fallback={<AlbumsGlimmer />}> <Panel> <Albums artistId={artist.id} /> </Panel> </Suspense> </Suspense> </> ); } function BigSpinner() { return <h2>🌀 ஏற்றுகிறது...</h2>; } function AlbumsGlimmer() { return ( <div className="glimmer-panel"> <div className="glimmer-line" /> <div className="glimmer-line" /> <div className="glimmer-line" /> </div> ); }
உங்கள் UI-யின் எந்த பகுதிகள் எப்போதும் ஒரே நேரத்தில் சேர்ந்து “pop in” ஆக வேண்டும், எந்த பகுதிகள் loading states-ன் sequence-இல் படிப்படியாக மேலும் content-ஐ வெளிப்படுத்த வேண்டும் என்பதை Suspense boundaries மூலம் ஒருங்கிணைக்கலாம். உங்கள் app-ன் மற்ற behavior-ஐ பாதிக்காமல் tree-இன் எந்த இடத்திலும் Suspense boundaries-ஐ சேர்க்கலாம், நகர்த்தலாம், அல்லது நீக்கலாம்.
ஒவ்வொரு component-ஐச் சுற்றியும் Suspense boundary வைக்க வேண்டாம். பயனர் அனுபவிக்க வேண்டும் என்று நீங்கள் நினைக்கும் loading sequence-ஐ விட Suspense boundaries அதிக granular ஆக இருக்கக்கூடாது. நீங்கள் designer உடன் பணிபுரிந்தால், loading states எங்கு வைக்கப்பட வேண்டும் என்று அவர்களிடம் கேளுங்கள்; அவர்கள் அதை ஏற்கனவே design wireframes-இல் சேர்த்திருக்க வாய்ப்பு உள்ளது.
புதிய content loading ஆகும்போது பழைய content-ஐக் காட்டுதல்
இந்த எடுத்துக்காட்டில், search results-ஐ fetch செய்யும் போது SearchResults component suspend ஆகிறது. "a" என type செய்து, results வர காத்திருந்து, பின்னர் அதை "ab" ஆக மாற்றுங்கள். "a"-க்கான results loading fallback-ஆல் மாற்றப்படும்.
import { Suspense, useState } from 'react'; import SearchResults from './SearchResults.js'; export default function App() { const [query, setQuery] = useState(''); return ( <> <label> ஆல்பங்களைத் தேடு: <input value={query} onChange={e => setQuery(e.target.value)} /> </label> <Suspense fallback={<h2>ஏற்றுகிறது...</h2>}> <SearchResults query={query} /> </Suspense> </> ); }
ஒரு பொதுவான மாற்று UI pattern என்பது பட்டியலை update செய்வதை defer செய்து, புதிய results தயாராகும் வரை முந்தைய results-ஐத் தொடர்ந்து காட்டுவது. useDeferredValue Hook query-ன் deferred version-ஐ கீழே pass செய்ய அனுமதிக்கிறது:
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
ஆல்பங்களைத் தேடு:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>ஏற்றுகிறது...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}query உடனடியாக update ஆகும், எனவே input புதிய value-ஐக் காட்டும். ஆனால் data load ஆகும் வரை deferredQuery அதன் முந்தைய value-ஐ வைத்திருக்கும், எனவே SearchResults சிறிது நேரம் பழைய results-ஐக் காட்டும்.
பயனருக்கு இது தெளிவாகத் தெரிய, பழைய result list காட்டப்படும் போது ஒரு visual indication சேர்க்கலாம்:
<div style={{
opacity: query !== deferredQuery ? 0.5 : 1
}}>
<SearchResults query={deferredQuery} />
</div>கீழே உள்ள எடுத்துக்காட்டில் "a" என உள்ளிட்டு, results load ஆக காத்திருந்து, பின்னர் input-ஐ "ab" ஆக மாற்றுங்கள். புதிய results load ஆகும் வரை Suspense fallback-க்கு பதிலாக மங்கலான பழைய result list தெரிகிறது என்பதை கவனியுங்கள்:
import { Suspense, useState, useDeferredValue } from 'react'; import SearchResults from './SearchResults.js'; export default function App() { const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); const isStale = query !== deferredQuery; return ( <> <label> ஆல்பங்களைத் தேடு: <input value={query} onChange={e => setQuery(e.target.value)} /> </label> <Suspense fallback={<h2>ஏற்றுகிறது...</h2>}> <div style={{ opacity: isStale ? 0.5 : 1 }}> <SearchResults query={deferredQuery} /> </div> </Suspense> </> ); }
ஏற்கனவே வெளிப்பட்ட content மறைக்கப்படுவதைத் தடுக்குதல்
ஒரு component suspend ஆகும்போது, அருகிலுள்ள parent Suspense boundary fallback-ஐக் காட்ட மாறும். அது ஏற்கனவே சில content-ஐக் காட்டிக் கொண்டிருந்தால், இது சீரற்ற user experience-ஐ உருவாக்கலாம். இந்த button-ஐ அழுத்திப் பாருங்கள்:
import { Suspense, useState } from 'react'; import IndexPage from './IndexPage.js'; import ArtistPage from './ArtistPage.js'; import Layout from './Layout.js'; export default function App() { return ( <Suspense fallback={<BigSpinner />}> <Router /> </Suspense> ); } function Router() { const [page, setPage] = useState('/'); function navigate(url) { setPage(url); } let content; if (page === '/') { content = ( <IndexPage navigate={navigate} /> ); } else if (page === '/the-beatles') { content = ( <ArtistPage artist={{ id: 'the-beatles', name: 'The Beatles', }} /> ); } return ( <Layout> {content} </Layout> ); } function BigSpinner() { return <h2>🌀 ஏற்றுகிறது...</h2>; }
நீங்கள் button-ஐ அழுத்தியபோது, Router component IndexPage-க்கு பதிலாக ArtistPage-ஐ render செய்தது. ArtistPage-க்குள் உள்ள ஒரு component suspend ஆனதால், அருகிலுள்ள Suspense boundary fallback-ஐக் காட்டத் தொடங்கியது. அருகிலுள்ள Suspense boundary root-க்கு அருகில் இருந்ததால், முழு site layout BigSpinner-ஆல் மாற்றப்பட்டது.
இதைக் தடுக்க, startTransition: மூலம் navigation state update-ஐ Transition ஆகக் குறிக்கலாம்:
function Router() {
const [page, setPage] = useState('/');
function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...state transition urgent அல்ல; ஏற்கனவே வெளிப்பட்ட content-ஐ மறைப்பதற்குப் பதிலாக முந்தைய page-ஐத் தொடர்ந்து காட்டுவது நல்லது என்று இது React-க்கு தெரிவிக்கிறது. இப்போது button-ஐ click செய்தால், Biography load ஆகும் வரை அது “காத்திருக்கும்”:
import { Suspense, startTransition, useState } from 'react'; import IndexPage from './IndexPage.js'; import ArtistPage from './ArtistPage.js'; import Layout from './Layout.js'; export default function App() { return ( <Suspense fallback={<BigSpinner />}> <Router /> </Suspense> ); } function Router() { const [page, setPage] = useState('/'); function navigate(url) { startTransition(() => { setPage(url); }); } let content; if (page === '/') { content = ( <IndexPage navigate={navigate} /> ); } else if (page === '/the-beatles') { content = ( <ArtistPage artist={{ id: 'the-beatles', name: 'The Beatles', }} /> ); } return ( <Layout> {content} </Layout> ); } function BigSpinner() { return <h2>🌀 ஏற்றுகிறது...</h2>; }
Transition அனைத்து content-மும் load ஆக காத்திருக்காது. ஏற்கனவே வெளிப்பட்ட content மறைக்கப்படுவதைத் தவிர்க்க தேவையான அளவு நேரம் மட்டுமே காத்திருக்கும். எடுத்துக்காட்டாக, website Layout ஏற்கனவே வெளிப்பட்டிருந்ததால், அதை loading spinner-க்கு பின்னால் மறைப்பது சரியாக இருக்காது. ஆனால் Albums-ஐச் சுற்றியுள்ள nested Suspense boundary புதியது, எனவே Transition அதற்காக காத்திருக்காது.
Transition நடந்து கொண்டிருப்பதைச் சுட்டிக்காட்டுதல்
மேலுள்ள எடுத்துக்காட்டில், button-ஐ click செய்த பிறகு navigation நடந்து கொண்டிருக்கிறது என்பதற்கான visual indication இல்லை. indicator ஒன்றைச் சேர்க்க, startTransition-க்கு பதிலாக boolean isPending value-ஐ தரும் useTransition-ஐப் பயன்படுத்தலாம். கீழே உள்ள எடுத்துக்காட்டில், Transition நடந்து கொண்டிருக்கும் போது website header styling-ஐ மாற்ற இது பயன்படுத்தப்படுகிறது:
import { Suspense, useState, useTransition } from 'react'; import IndexPage from './IndexPage.js'; import ArtistPage from './ArtistPage.js'; import Layout from './Layout.js'; export default function App() { return ( <Suspense fallback={<BigSpinner />}> <Router /> </Suspense> ); } function Router() { const [page, setPage] = useState('/'); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } let content; if (page === '/') { content = ( <IndexPage navigate={navigate} /> ); } else if (page === '/the-beatles') { content = ( <ArtistPage artist={{ id: 'the-beatles', name: 'The Beatles', }} /> ); } return ( <Layout isPending={isPending}> {content} </Layout> ); } function BigSpinner() { return <h2>🌀 ஏற்றுகிறது...</h2>; }
navigation-இல் Suspense boundaries-ஐ reset செய்தல்
Transition நடக்கும் போது, ஏற்கனவே வெளிப்பட்ட content மறைக்கப்படுவதை React தவிர்க்கும். ஆனால் வேறு parameters உள்ள route-க்கு navigate செய்தால், அது வேறு content என்று React-க்கு தெரிவிக்க வேண்டியிருக்கலாம். இதை key மூலம் வெளிப்படுத்தலாம்:
<ProfilePage key={queryParams.id} />ஒரு பயனரின் profile page-க்குள் நீங்கள் navigate செய்கிறீர்கள், அப்போது ஏதாவது suspend ஆகிறது என்று கற்பனை செய்யுங்கள். அந்த update Transition-க்குள் wrap செய்யப்பட்டிருந்தால், ஏற்கனவே தெரியும் content-க்கு fallback-ஐ trigger செய்யாது. அதுதான் எதிர்பார்க்கப்படும் behavior.
ஆனால் இப்போது நீங்கள் இரண்டு வெவ்வேறு user profiles-க்கு இடையில் navigate செய்கிறீர்கள் என்று கற்பனை செய்யுங்கள். அப்படியானால் fallback-ஐக் காட்டுவது பொருத்தமானது. எடுத்துக்காட்டாக, ஒரு user-ன் timeline மற்றொரு user-ன் timeline-இலிருந்து வேறு content ஆகும். key குறிப்பிடுவதன் மூலம், வெவ்வேறு users-ன் profiles-ஐ React வெவ்வேறு components ஆக நடத்தி, navigation நடக்கும் போது Suspense boundaries-ஐ reset செய்வதை உறுதி செய்கிறீர்கள். Suspense-integrated routers இதை தானாகச் செய்ய வேண்டும்.
server errors மற்றும் client-only content-க்கு fallback வழங்குதல்
நீங்கள் streaming server rendering APIs-இல் ஒன்றையோ (அவற்றை நம்பியிருக்கும் framework ஒன்றையோ) பயன்படுத்தினால், server-இல் errors-ஐ handle செய்ய React உங்கள் <Suspense> boundaries-ஐயும் பயன்படுத்தும். ஒரு component server-இல் error throw செய்தால், React server render-ஐ abort செய்யாது. அதற்கு பதிலாக, மேலுள்ள அருகிலுள்ள <Suspense> component-ஐ கண்டுபிடித்து, அதன் fallback-ஐ (spinner போன்றது) உருவாக்கப்பட்ட server HTML-இல் சேர்க்கும். பயனர் முதலில் spinner-ஐப் பார்ப்பார்.
client-இல், அதே component-ஐ React மீண்டும் render செய்ய முயலும். அது client-இலும் error ஆனால், React error-ஐ throw செய்து, அருகிலுள்ள Error Boundary-ஐக் காட்டும். ஆனால் client-இல் error ஆகவில்லை என்றால், content இறுதியில் வெற்றிகரமாகக் காட்டப்பட்டதால் React அந்த error-ஐ பயனருக்கு காட்டாது.
சில components server-இல் render ஆகாமல் opt out செய்ய இதைப் பயன்படுத்தலாம். இதற்கு server environment-இல் error throw செய்து, பின்னர் அவற்றின் HTML-ஐ fallbacks-ஆல் மாற்ற <Suspense> boundary-க்குள் wrap செய்யவும்:
<Suspense fallback={<Loading />}>
<Chat />
</Suspense>
function Chat() {
if (typeof window === 'undefined') {
throw Error('Chat client-இல் மட்டுமே render செய்ய வேண்டும்.');
}
// ...
}server HTML-இல் loading indicator இருக்கும். client-இல் அது Chat component-ஆல் மாற்றப்படும்.
Troubleshooting
update நடக்கும் போது UI fallback-ஆல் மாற்றப்படுவதை எப்படி தடுக்கலாம்?
தெரியும் UI-ஐ fallback-ஆல் மாற்றுவது சீரற்ற user experience-ஐ உருவாக்கும். ஒரு update component-ஐ suspend செய்யும்போது, அருகிலுள்ள Suspense boundary ஏற்கனவே பயனருக்கு content-ஐக் காட்டிக் கொண்டிருந்தால் இது நடக்கலாம்.
இது நடக்காமல் தடுக்க, startTransition பயன்படுத்தி update-ஐ non-urgent எனக் குறியிடுங்கள். Transition நடக்கும் போது, தேவையற்ற fallback தோன்றுவதைத் தடுக்க போதுமான data load ஆகும் வரை React காத்திருக்கும்:
function handleNextPageClick() {
// If this update suspends, don't hide the already displayed content
startTransition(() => {
setCurrentPage(currentPage + 1);
});
}இது ஏற்கனவே உள்ள content மறைக்கப்படுவதைத் தவிர்க்கும். ஆனால் புதிதாக render செய்யப்பட்ட எந்த Suspense boundaries-மும் UI-ஐ block செய்யாமல், content கிடைக்கும் போதே பயனர் பார்க்க அனுமதிக்க உடனடியாக fallbacks-ஐக் காட்டும்.
non-urgent updates நடக்கும் போது மட்டுமே React தேவையற்ற fallbacks-ஐத் தடுக்கும். urgent update-ன் விளைவாக render வந்தால், React அதை தாமதப்படுத்தாது. startTransition அல்லது useDeferredValue போன்ற API மூலம் நீங்கள் opt in செய்ய வேண்டும்.
உங்கள் router Suspense உடன் integrated ஆக இருந்தால், அது அதன் updates-ஐ தானாகவே startTransition-க்குள் wrap செய்ய வேண்டும்.