기본적으로, Suspense
내부의 전체 트리는 하나의 단위로 처리됩니다.
예를 들어, 이 컴포넌트들 중 하나만 데이터를 기다리는 동안 일시 중단되더라도, 모두 함께 로딩 표시기로 대체될 것입니다.
<Suspense fallback={<Loading />}>
<Biography />
<Panel>
<Albums />
</Panel>
</Suspense>
그런 다음, 모두 표시할 준비가 되면 한꺼번에 모두 함께 나타납니다.
아래 예시에서 <Biography/>
와 <Albums/>
모두 데이터를 가져옵니다. 그러나 하나의 Suspense 경계
아래에 그룹화되어 있기 때문에 이러한 컴포넌트들은 항상 동시에 등장합니다.
데이터를 로드하는 컴포넌트들은 반드시 Suspense 경계
의 직접적인 자식이어야 하는 것은 아닙니다. 예를 들어, <Biography/>
와 <Albums/>
를 새로운 Details 컴포넌트로 이동할 수 있습니다.
이는 마찮가지로 동시에 등장합니다.
*<Biography/>
와 <Albums/>
는 동일한 가장 가까운 부모 Suspense 경계
를 공유하기 때문에, <Biography/>
와 <Albums/>
의 공개가 함께 조정됩니다.*
<Suspense fallback={<Loading />}>
<Details artistId={artist.id} />
</Suspense>
function Details({ artistId }) {
return (
<>
<Biography artistId={artistId} />
<Panel>
<Albums artistId={artistId} />
</Panel>
</>
);
}
<aside> ⚠️ *동시에 여러 데이터를 가져올 때 Suspense와 함께 사용 시 fallback 컴포넌트로 대체되는 시간이 길어지는 이유
(Biography, Albums 라는 비동기 요청이 있다고 가정)
위 예제를 참고하여 비동기 요청을 통해 서버에서 데이터를 가져올 때(useQuery) <Details/>
컴포넌트에서 Biography async 요청과 Albums async 요청을 하였다면 직렬로 수행되기 때문에 Suspense의 fallback의 유지 시간 즉 로딩 시간이 길어진다.*
function Details({ artistId }) {
const { data } = useQuery('/biography', () => client.get(`/biography${artistId}`), {
suspense: true
});
const { data } = useQuery('/albums', () => client.get(`/albums${artistId}`), {
suspense: true
});
return (
// ...
);
}
*<Biography/>
에서 Biography async 요청을 수행하고, <Albums/>
에서 Albums async 요청을 수행하면 각각의 컴포넌트에서 비동기 요청이 수행되므로 병렬로 비동기 요청이 수행되기 때문에 Suspense의 대체 내용의 유지 시간이 짧아 진다. 즉 로딩 시간이 짧아 진다.*
function Biography({ artistId }) {
const { data } = useQuery('/biography', () => client.get(`/biography${artistId}`), {
suspense: true
});
// ...
}
function Albums({ artistId }) {
const { data } = useQuery('/albums', () => client.get(`/albums${artistId}`), {
suspense: true
});
// ...
}
정확히 말하자면 useQuery + suspense mode 사용 시 water fall
이 발생하는데 그 이유는, Promise가 throw 될 때 마다 해당 Promise를 반환하는 코드 다음 줄이 런타임에서 실행되기 이전에 컴포넌트가 언마운트되기 때문입니다.
</aside>
<aside> 💡 참고: 현재 useQueries() v4.x 에서 suspense property를 지원합니다.
</aside>