<Suspense>는 컴포넌트 렌더링을 지연시키고 대체 UI를 보여주는 기능이다. 비동기 작업을 처리할 때 유용하게 사용할 수 있으며, 주로 데이터 로딩이나 코드 스플리팅 같은 작업에 사용된다.
로딩으로 인한 딜레이 중 사용자 경험을 개선할 수 있다. 빈 화면 대신 로딩 화면을 보여주며 사용자가 멈춤 현상을 경험하지 않게 한다.
지연을 이용해 초기에 필요한 컴포넌트만 로드하여 초기 로딩 속도를 개선할 수 있다.
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
children : 렌더링하려는 실제 UI이다. children의 렌더링이 지연될 때, Suspense는 fallback을 대신 렌더링한다.
fallback : 대체 UI이다. children의 렌더링이 지연될 때 대신 렌더링되다가, 데이터가 준비됐을 때 children으로 다시 전환된다. 만약 fallback의 렌더링이 지연된다면 가장 가까운 부모 Suspense가 렌더링된다.
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>Loading...</h2>;
}
위 코드는 앨범 데이터가 로딩되는 동안 Loading 컴포넌트를 표시한다. 로드가 끝났을 땐 Loading을 숨기고 다시 Albums 컴포넌트를 렌더링한다.
<Suspense fallback={<BigSpinner />}>
<Biography />
<Suspense fallback={<AlbumsGlimmer />}>
<Panel>
<Albums />
</Panel>
</Suspense>
</Suspense>
여러 Suspense를 중첩하여 로딩 순서를 만들 수도 있다.
Biography가 로드되지 않은 경우, BigSpinner가 표시된다.Albums가 로드되지 않은 경우, AlbumsGlimmer가 표시된다.useDefferredValue는 사용자가 빠르게 입력을 변경할 때, 결과가 즉시 업데이트되지 않고 약간 지연되도록 도와준다. 사용자 경험을 개선하고 불필요한 렌더링을 줄이는데 도움이 된다.
지연 시간은 고정되어 있지 않으며, React에 여유가 있을 때 업데이트된다. 브라우저의 현재 상태나 작업 부하에 따라 달라질 수 있다.
useDeferredValue를 사용하면 쿼리의 지연된 버전을 아래로 전달할 수 있다.
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>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<SearchResults query={deferredQuery} />
</div>
</Suspense>
</>
);
}
위 예제는 검색 결과를 가져오는 동안 SearchResult가 지연되며, 지연되는 동안 이전 검색 결과를 계속해서 보여준다.
예를 들어 a를 검색하고 ab를 검색하면, ab의 결과를 가져오는 동안 a의 검색 결과를 투명도 50%로 보여준다.
lazy는 컴포넌트가 렌더링 될 때까지 로딩을 연기할 수 있다.
import { useState, Suspense, lazy } from 'react';
import Loading from './Loading.js';
const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));
export default function MarkdownEditor() {
const [showPreview, setShowPreview] = useState(false);
const [markdown, setMarkdown] = useState('Hello, **world**!');
return (
<>
<textarea value={markdown} onChange={e => setMarkdown(e.target.value)} />
<label>
<input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} />
Show preview
</label>
{showPreview && (
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview markdown={markdown} />
</Suspense>
)}
</>
);
}
위 예제는 checkbox가 체크되기 전까지 MarkdownPreview가 나타나지 않기 때문에, lazy를 이용해 지연시켰다.