데이터를 가져올 때, 데이터가 도착하기 전에 페이지를 보여주거나 속도가 다른 다수의 HTTP 요청이 있는 페이지에서 페이지의 일부를 보여주려 할 때가 있을 수 있다.
이렇게 데이터가 다 도착하지 않았어도 컴포넌트를 렌더링하라고 리액트 라우터에게 알릴 수 있다.
defer
그러기 위해서 loader 함수에서 defer 함수를 실행 후 리턴해야 하고, 그 페이지에서 오갈 수 있는 모든 HTTP 요청을 넣은 객체를 defer함수의 인수로 전달해야 한다.
async function loadEvents() {
const response = await fetch('http://localhost:8080/events');
if (!response.ok) {
throw json({ message: 'Could not fetch events' }, { status: 500 });
} else {
const resData = await response.json();
return resData.events;
}
}
export function loader() {
return defer({
events: loadEvents(),
});
}
함수에 전달하는 객체의 key는 우리가 정할 수 있고, 값으로는 실제 HTTP 요청을 보내는 loadEvents 함수를 실행한다.
✔️ 주의할 것은 loadeEvent를 지시하는 것이 아니라 실행해야 한다!!
그러면 loadEvents를 실행하고 리턴한 값을 events에 저장하게 된다. 즉, 여기선 loadEvents가 async function 이기 때문에 Promise가 있어야 한다.
🌟 defer라는 개념은 다른 값으로 resolving될 어떤 값이 있다는 걸 가정하기 때문에 무조건 Promise를 리턴하는 함수를 전달해야 한다!!
Await 컴포넌트
연기된 데이터를 사용하려는 컴포넌트에서 그 데이터를 필요로 하는 컴포넌트를 직접 렌더링하지 않고 react-router-dom이 제공하는 Await 컴포넌트를 사용해야 한다.
const data = useLoaderData();
return (
<Suspense fallback={<p style={{ textAlign: 'center' }}>Loading...</p>}>
<Await resolve={data.events}>
{(loadedEvents) => <EventsList events={loadedEvents} />}
</Await>
</Suspense>
);
Await 컴포넌트는 resolve prop을 갖고 있는데 여기엔 연기된 값을 넣어준다. data 객체는 defer에 넣은 객체에 설정한 키를 갖고 있다.
그리고 그 events 키는 Promise를 값으로 갖게 되고 이 resolve prop에 전달하는 건 그 Promise이다.
Await 컴포넌트는 resolve에 넣어준 데이터가 올 때까지 기다리고, 데이터가 도착하면 시작 태그와 종료 태그 사이에 넣어준 함수가 리액트 라우터에 의해 실행된다.
{(loadedEvents) => <EventsList events={loadedEvents} />}
즉, 이 함수는 resolve된 데이터, 즉 events를 받고 리액트 라우터에 의해 실행된다.
또한 Await 컴포넌트를 감싸는 Suspense 컴포넌트를 추가해야 하는데, 이 컴포넌트는 react에서 import한다.
Suspense 컴포넌트는 다른 데이터가 도착하길 기다리는 동안에 fallback을 보여주는 상황에서 사용할 수 있다.
defer에 다수의 요청 전달하기
export async function loader({ request, params }) {
const id = params.eventId;
return defer({
event: loadEvent(id),
events: loadEvents(),
});
}
defer에 전달한 객체의 프로퍼티(키)로 event와 events를 전달했기 때문에
const { event, events } = useRouteLoaderData('event-detail');
연기된 데이터를 받을 때 위 코드와 같이 구조 분해 할당으로 event와 events를 받을 수 있다.
return (
<>
<Suspense fallback={<p>Loading...</p>}>
<Await resolve={event}>
{(loadedEvent) => <EventItem event={loadedEvent} />}
</Await>
</Suspense>
<Suspense fallback={<p>Loading...</p>}>
<Await resolve={events}>
{(loadedEvents) => <EventsList events={loadedEvents} />}
</Await>
</Suspense>
</>
);
Await 컴포넌트를 두 번 사용해서 이 두 가지 요청을 기다리고 데이터가 도착하면 각각 EventItem과 EventsList를 출력할 것이다.
export async function loader({ request, params }) {
const id = params.eventId;
return defer({
event: await loadEvent(id),
events: loadEvents(),
});
}
만약 defer에 전달하는 객체 안에서 await 키워드로 함수를 실행한다면 defer는 await 키워드로 호출한 함수(loadEvent)가 리턴하는 데이터가 로딩될 때까지 기다렸다가 페이지 컴포넌트를 로딩하게 되고, 페이지를 로딩한 다음에 loadEvents 데이터를 로딩하게 된다.
이렇게 defer 안에서 await 키워드를 사용하면 페이지로 이동하기 전 어떤 데이터를 기다려야 하는지, 페이지로 이동한 다음에 어떤 데이터를 로딩해야 하는지를 제어할 수 있게 된다.
즉 defer 함수를 사용해서 언제, 어떤 데이터를 로딩할 지 제어할 수 있다!
'React' 카테고리의 다른 글
[React] TanStack Query (React-Query) (0) | 2023.09.30 |
---|---|
[React] lazy loading (0) | 2023.09.28 |
[React] useFetcher (0) | 2023.09.27 |
[React] React Router - action, 데이터 전송하기 (0) | 2023.09.26 |
[React] React Router - loader, useLoaderData로 데이터 가져오기 (0) | 2023.09.26 |