// loader()
- 서버에서 데이터를 가져온 후 컴포넌트를 렌더링할 수 있게 도와주는 react-router의 기능
- react-router 버전 6 이상을 사용해야 한다.
1. 데이터를 가져오는 페이지(컴포넌트)의 route definition에 loader property를 추가한다.
{ path: 'url', element: <컴포넌트 />, loader: () => {
// 데이터 가져오기
return data;
}},
- loader는 함수를 값으로 가진다.
- loader의 return 값은 element 컴포넌트와 해당 컴포넌트를 필요로 하는 모든 컴포넌트들(childern)에 전달된다.
* loader 함수 처리가 너무 복잡해지기 때문에, element 컴포넌트에 loader 함수를 넣을 수도 있다.
export async function loader함수명() {
// data 가져오기
return data;
}
2. 원하는 컴포넌트에서 loader 에서 return한 data를 사용한다.
const data명 = useLoaderData();
import 컴포넌트명, { loader as loader명 } from '경로';
{
index: true,
element: <컴포넌트명 />,
loader: loader명,
},
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import RootLayout from './pages/Root';
import Homepage from './pages/Home';
import EventsPage, { loader as eventsLoader } from './pages/Events';
import EventDetailPage from './pages/EventDetail';
import EditEventPage from './pages/EditEvent';
import NewEventPage from './pages/NewEvent';
import EventsRootLayout from './pages/EventsRoot';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Homepage /> },
{
path: 'events',
element: <EventsRootLayout />,
children: [
{
index: true,
element: <EventsPage />,
loader: eventsLoader,
},
{ path: ':eventId', element: <EventDetailPage /> },
{ path: 'new', element: <NewEventPage /> },
{ path: ':eventId/edit', element: <EditEventPage /> },
],
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
import React from 'react';
import EventsList from '../components/EventsList';
import { useLoaderData } from 'react-router-dom';
import { getEvents } from '../plugins/eventAxios';
function EventsPage() {
const events = useLoaderData();
return <>{<EventsList events={events} />}</>;
}
export default EventsPage;
export async function loader() {
const data = await getEvents();
return data.result;
}
// useNavigation
- loader에서 데이터를 가져오는 중인지 확인할 수 있게 해준다.
const navitgation = useNavigation();
const isLoading = navitgation.state === 'loading';
- loader에서 데이터를 가져올 때 발생하는 error를 받아오는 hook
1. 오류 throw
throw new Response(JSON.stringify({ message: '메시지' }), {
status: status코드,
});
//json() 사용
json(
{ message: 'Could not fetch Events' },
{
status: 500,
},
);
- json() 함수가 Response 객체를 생성해준다.
2. 오류 정보 받기
const error = useRouteError();
//Response 객체 return한 경우
const message = JSON.parse(error.data).message;
// json 함수 사용한 경우
const message = error.data.message;
import React from 'react';
import EventsList from '../components/EventsList';
import { useLoaderData } from 'react-router-dom';
import { getEvents } from '../plugins/eventAxios';
function EventsPage() {
const events = useLoaderData();
return <>{<EventsList events={events} />}</>;
}
export default EventsPage;
export async function loader() {
const data = await getEvents();
if (data.status === 'FAIL') {
throw new Response(JSON.stringify({ message: 'Could not fetch Events' }), {
status: 500,
});
} else if (data.status === 'SUCCESS') {
return data.result;
}
}
import React from 'react';
import PageContent from '../components/PageContent';
import { useRouteError } from 'react-router-dom';
function ErrorPage() {
const error = useRouteError();
let title = 'An error occurred!';
let message = 'Something went wrong!';
if (error.status === 500) {
message = JSON.parse(error.data).message;
}
if (error.status === 404) {
title = 'Not found';
message = 'Could not find resource or page.';
}
return (
<PageContent title={title}>
<p>{message}</p>
</PageContent>
);
}
export default ErrorPage;
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import RootLayout from './pages/Root';
import Homepage from './pages/Home';
import EventsPage, { loader as eventsLoader } from './pages/Events';
import EventDetailPage from './pages/EventDetail';
import EditEventPage from './pages/EditEvent';
import NewEventPage from './pages/NewEvent';
import EventsRootLayout from './pages/EventsRoot';
import Error from './pages/Error';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <Error />,
children: [
{ index: true, element: <Homepage /> },
{
path: 'events',
element: <EventsRootLayout />,
children: [
{
index: true,
element: <EventsPage />,
loader: eventsLoader,
},
{ path: ':eventId', element: <EventDetailPage /> },
{ path: 'new', element: <NewEventPage /> },
{ path: ':eventId/edit', element: <EditEventPage /> },
],
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
// 동적 route + loader
- 동적 route에서 loader 함수를 정의할 때, request와 params를 받을 수 있다.
- params를 통해 동적 segement에 접근할 수 있다.
export async function loader함수명({ request, params }) {
const id = params.식별자명;
}
// useRouteLoaderData
- 자신의 route가 아닌 부모 route의 data를 받아올 때 사용한다.
1. route 정의
{
path: ,
loader: ,
id: ,
children: [
{
index: true,
element: < />,
},
],
},
- 부모의 loader에서 data를 받아오려면 id property를 추가해야 한다.(이름은 자유)
2. data 받기
const 변수명 = useRouteLoaderData('routeId');
import { getEventDetail } from '../plugins/eventAxios';
import { json, useRouteLoaderData } from 'react-router-dom';
import EventItem from '../components/EventItem';
function EventDetailPage() {
const event = useRouteLoaderData('event-detail');
return <>{<EventItem event={event} />}</>;
}
export default EventDetailPage;
export async function loader({ request, params }) {
const id = params.eventId;
const data = await getEventDetail(id);
if (data.status === 'FAIL') {
json(
{ message: 'Could not fetch Event detail' },
{
status: 500,
},
);
} else if (data.status === 'SUCCESS') {
return data.result.event;
}
}
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import RootLayout from './pages/Root';
import Homepage from './pages/Home';
import EventsPage, { loader as eventsLoader } from './pages/Events';
import EventDetailPage, {
loader as eventDetailLoader,
} from './pages/EventDetail';
import EditEventPage from './pages/EditEvent';
import NewEventPage from './pages/NewEvent';
import EventsRootLayout from './pages/EventsRoot';
import Error from './pages/Error';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <Error />,
children: [
{ index: true, element: <Homepage /> },
{
path: 'events',
element: <EventsRootLayout />,
children: [
{
index: true,
element: <EventsPage />,
loader: eventsLoader,
},
{
path: ':eventId',
loader: eventDetailLoader,
id: 'event-detail',
children: [
{
index: true,
element: <EventDetailPage />,
},
{ path: 'edit', element: <EditEventPage /> },
],
},
{ path: 'new', element: <NewEventPage /> },
],
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
// defer
- Promise를 연기한다.
1. loader 함수 정의
export async function loader() {
return defer({
키: Promise를반환하는함수()
});
}
2. defer된 데이터 사용
function 컴포넌트명() {
const 변수명 = useLoaderData().키;
return (
<Suspense fallback={jsx코드}>
<Await resolve={변수명}>
{(promise완료data) => jsx코드}
</Await>
</Suspense>
);
}
- defer함수에서 설정한 key로 data에 접근한다.
- data를 필요로 하는 컴포넌트나 jsx코드를 직접 렌더링하지 않고 Await 컴포넌트를 렌더링한다.
- Await 태그의 resolve에 응답을 기다리는 data를 설정한다.
- Await 태그 사이에 data를 받아온 후 실행할 함수를 넣어준다.
- Suspense 컴포넌트는 다른 data가 도착하길 기다리는 동안 fallback을 보여준다.
'클라이언트 > React' 카테고리의 다른 글
[리액트(React)] Router - useFetcher (0) | 2024.02.21 |
---|---|
[리액트(React)] Router - action (0) | 2024.02.20 |
[리액트(React)] 동적 Route (0) | 2024.02.18 |
[리액트(React)] 라우터(Router) (0) | 2024.02.17 |
[리액트(React)] Redux Toolkit (0) | 2024.02.16 |