loader() 사용
loader() 사용 전
import { useEffect, useState } from 'react';
import EventsList from '../components/EventsList';
function EventsPage() {
const [isLoading, setIsLoading] = useState(false);
const [fetchedEvents, setFetchedEvents] = useState();
const [error, setError] = useState();
useEffect(() => {
async function fetchEvents() {
setIsLoading(true);
const response = await fetch('http://localhost:8080/events');
if (!response.ok) {
setError('Fetching events failed.');
} else {
const resData = await response.json();
setFetchedEvents(resData.events);
}
setIsLoading(false);
}
fetchEvents();
}, []);
return (
<>
<div style={{ textAlign: 'center' }}>
{isLoading && <p>Loading...</p>}
{error && <p>{error}</p>}
</div>
{!isLoading && fetchedEvents && <EventsList events={fetchedEvents} />}
</>
);
}
export default EventsPage;
컴포넌트 렌더링 전에 실행하는 함수가 없어 컴포넌트 내에 들어갈 데이터를 컴포넌트 내에 작성
loader() 사용 후
App.jsx
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import EditEventPage from './pages/EditEvent';
import ErrorPage from './pages/Error';
import EventDetailPage, {
loader as eventDetailLoader,
action as deleteEventAction,
} from './pages/EventDetail';
import EventsPage, { loader as eventsLoader } from './pages/Events';
import EventsRootLayout from './pages/EventsRoot';
import HomePage from './pages/Home';
import NewEventPage from './pages/NewEvent';
import RootLayout from './pages/Root';
import { action as manipulateEventAction } from './components/EventForm';
import NewsletterPage, { action as newsletterAction } from './pages/Newsletter';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <ErrorPage />,
children: [
{ index: true, element: <HomePage /> },
{
path: 'events',
element: <EventsRootLayout />,
children: [
{
index: true,
element: <EventsPage />,
loader: eventsLoader,
},
{
path: ':eventId',
// id를 등록하면 child 라우터에서도 loader데이터 사용 가능
// 컴포넌트에서 const data = useRouteLoaderData('event-detail')로 데이터 가져올 수 있음
id: 'event-detail',
// EventDetailPage, EditEventPage에서 둘다 loader에서 return 되는
// 데이터를 사용할 수 있음
loader: eventDetailLoader,
children: [
{
index: true,
element: <EventDetailPage />,
action: deleteEventAction,
},
{
path: 'edit',
element: <EditEventPage />,
action: manipulateEventAction,
},
],
},
{
path: 'new',
element: <NewEventPage />,
action: manipulateEventAction,
},
],
},
{
path: 'newsletter',
element: <NewsletterPage />,
action: newsletterAction,
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
Events.jsx
import { useEffect, useState } from 'react';
import {useLoaderData} from 'react-router-dom';
import EventsList from '../components/EventsList';
function EventsPage() {
const data = useLoaderData();
if(data.isError){
return <p>{data.message}</p>
}
const events = data.events;
return (
<>
<EventsList events={events} />}
</>
);
}
export default EventsPage;
export function async loader(){
// 실제로 Response로 리졸빙되는 Promise 리턴
const response = await fetch('http://localhost:8080/events');
if (!response.ok) {
return {isError: true, message: 'Error'}
} else {
return response;
}
}
- loader : 함수를 값으로 취하는 프로퍼티
- 리액트 라우터는 항상 컴포넌트 렌더링 직전(페이지로 이동하기 전)에 loader 함수를 방문
- async 함수를 사용하는 경우 리액트 라우터는 자동으로 Promise 가 return 된건지 확인하고 Promise로 부터 리졸빙 된 데이터를 받아옴 > 개발자 입장에서는 Promise가 리턴되었는지를 신경 쓸 필요 없음
- 리액트 라우터는 loader가 작업을 완료할때 까지 대기하고 완료 되면 페이지 렌더링
- loader 는 서버에서 실행되지 않고 브라우저에서 실행됨
- loacer 내에서는 리액트 훅을 사용할 수 없음. 컴포넌트가 아니기 떄문
리액트 라우터 버전 6 이상
error 처리 방법
1. 객체로 보내기
import { useEffect, useState } from 'react';
import {useLoaderData} from 'react-router-dom';
import EventsList from '../components/EventsList';
function EventsPage() {
const data = useLoaderData();
if(data.isError){
return <p>{data.message}</p>
}
const events = data.events;
return (
<>
<EventsList events={events} />}
</>
);
}
export default EventsPage;
export function async loader(){
// 실제로 Response로 리졸빙되는 Promise 리턴
const response = await fetch('http://localhost:8080/events');
if (!response.ok) {
return {isError: true, message: 'Error'}
} else {
return response;
}
}
2. throw Error 던지기
import { useEffect, useState } from 'react';
import {useLoaderData} from 'react-router-dom';
import EventsList from '../components/EventsList';
function EventsPage() {
const data = useLoaderData();
const events = data.events;
return (
<>
<EventsList events={events} />}
</>
);
}
export default EventsPage;
export function async loader(){
// 실제로 Response로 리졸빙되는 Promise 리턴
const response = await fetch('http://localhost:8080/events');
if (!response.ok) {
// 방법 1
//throw new Response(JSON.stringify({message: 'Could not fetch events'}),{status: 500})
// 방법 2
return json({message: 'Could not fetch events'},{status: 500})
} else {
return response;
}
}
- 404 Error 될 시, 라우터에 입력한 ErrorElement 화면에 표시
- 객체를 생성해서 Error Response를 던짐
- json()은 react-router-dom에서 import 할 수 있음 , json 형식의 데이터가 포함된 Response객체를 생성하는 함수, 대신 값을 Parsing 해줌
ErrorPage.jsx
import { useRouteError } from "react-router-dom";
import PageContent from "../components/PageContent";
export default function ErrorPage(){
const error = useRouteError();
let title = 'An error occured'
let message = 'Something went wrong!';
if(error.status === 500){
// 방법 1
message = JSON.parse(error.data).message;
// 방법 2(json 사용 시 router가 대신 파싱해줌)
message = 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>
)
}
loader에서 throw 한 Error 객체를 라우터에 등록한 에러페이지에서 받을 수 있음
param 사용
import { useLoaderData, useParams } from "react-router-dom";
import EventItem from '../components/EventItem';
function EventDetailPage(){
const data = useLoaderData();
return <EventItem event={data.event} />
}
export default EventDetailPage;
export async function loader({request, params}){
const id = params.eventId; //router 에 등록한 param 이름
const response = await fetch('http://localhost:8080/events/' + id);
if(!response.ok){
return json({message: 'Could not fetch'},{status:500})
}
return response;
}
- 리액트 파라미터에 접근 가능 : loader() 함수를 호출하는 리액트 라우터가 그걸 실행할 때 실제로 객체 하나를 loader 함수에 전달함 request(요청 객체 url등의 정보) params(파라미터)
useNavigation
라우터 전환상태 확인 가능
import MainNavigation from "../components/MainNavigation";
import {Outlet, useNavigation} from 'react-router-dom'
function RootLayout(){
const navigation = useNavigation();
return <>
<MainNavigation />
<main>
{navigation.state === 'loading'&&<p>Loading...</p>}
</main>
</>
}
export default RootLayout;
전환상태가 로딩 중이면 Loading... 띄우기
주의 : 이미 화면에 표시되어있는 페이지 혹은 컴포넌트에 뜨게 됨
'React' 카테고리의 다른 글
[React] 인증 (1) | 2024.05.02 |
---|---|
[React] Router , action, defer() (0) | 2024.04.30 |
[React] router (0) | 2024.04.23 |
[React] Redux (0) | 2024.04.19 |
[React] Form Validation check (0) | 2024.04.18 |