전에는 너무 얕게만 라우터를 본거같아서 이번에는 좀더 세세하게 다루어 보고자한다
createBrowserRouter
loaders, actions, fetchers등을 사용할 수 있다
- 동적 체이지에 적합
- 검색엔진 최적화
- github-pages 배포가 까다로움
RouterProveder
어떤 Router 객체이든 Router 객체 RouterProvider에 등록되야한다
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
Outlet
컴포넌트의 children과 같은 개념으로 중첩이 가능해졌고 더 직관적으로 사용하기 위하여 나왔다
하위 경로 요소를 렌더링하려면 상위 경로 요소에서 사용해야한다
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{ path: "/", element: <Home /> },
{ path: "/products", element: <Products /> },
],
},
]);
///
const RootLayout = () => {
return (
<div>
<MainNav />
<Outlet></Outlet>
</div>
);
};
errorElement(v5에는 없고, v6에는 있음)
컴포넌트 에러가 발생해서 충돌하거나 컴포넌트의 위치를 찾지 못할 때 사용하며 다른 컴포넌트들에서 발생하는 문제로부터 보호해준다,
이상한 주소로 이동했을 경우에도 사용
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <Error />,
children: [
{ path: "/", element: <Home /> },
{ path: "/product", element: <Product /> },
],
},
]);
NavLink
URL이 'to' 에 지정된 URL과 일치할 시, 특정 스타일을 적용할 수 있고, Route의 path 처럼 동작하기 때문에 end 키워드가 있다
style 이나 className 을 지정할때는 콜백함수의 인자로 { isActive } 속성을 전달받으면 된다
<NavLink
to="/products"
end
className={({ isActive }) =>
isActive ? classes.active : undefined
}
>
여기서 end는 끝에만 일치하도록 논리를 변경한다=> 기본적으로 부분적으로 일치하는 경우에도 해당 경로가 일치로 간주 되기 때문이다
index
특정 경로에 대한 기본 컴포넌트를 나타낸다 해당 경로에 진입 할 때 특별한 하위 경로 없이 엔더링할 컴포넌트를 지정하는 역할이다
{
path : '/' ,
element : <DefaultLayout /> ,
children : [
{ index:true , element : <Home /> },
{ path:'sub' , element : <Sub /> },
]
}
만약 접속하면 DefaultLayout 기준으로 들어가게되는데 children의 path 에도 '/' 을 넣게 된다면 경로가 http://localhost:3000// 으로 선언이 되어버리는 현상이 나타난다 그렇기 때문에 path 를 넣는것이 아닌 / 으로 접속을 했을 때 이 레이아웃 밑에 이 페이지를 보여달라는 식으로 index : true 를 적어주면 루트 경로로 이동을 했을 때 문제없이 렌더링이 된다
loder
서버에 요청하지 않고 데이터를 가능한 빨리 가져오기 위해 제공하고 있다
로더는 컴포넌트가 렌더링되기 전에 호출되며 로더 함수가 값을 리턴하면 useLoaderData()로 컴포넌트에서 데이터를 받아 사용할 수 있다
children: [
{ index: true, element: <Home /> },
{
path: "events",
element: <EventRoot />,
children: [
{
index: true,
element: <Event />,
loader: async () => {
const response = await fetch("http://localhost:8080/events");
if (!response.ok) {
} else {
const resData = await response.json();
return resData;
}
},
},
//
import { useLoaderData } from "react-router-dom";
import EventsList from "../components/EventsList";
const Event = () => {
const { events } = useLoaderData();
return <EventsList events={events} />;
};
export default Event;
useRouteError
loader에서 throw new Response로 발생시킨 에러데이터를 받을수 있다
function ErrorBoundary() {
const error = useRouteError();
console.error(error);
return <div>{error.message}</div>;
}
*json utility
수작업으로 Reponse를 생성하는건 너무 귀찮아서 나온 helper utility이다
파싱도 안해도 돼서 간편하다
new Response(JSON.stringify(someValue), {
headers: {
"Content-Type": "application/json; utf-8",
},
});
import { json } from "react-router-dom";
const loader = async () => {
const data = getSomeData();
return json(data);
};
useRouteLoaderData
현재 렌더링된 경로 데이터를 트리의 어느 곳에서나 사용할수 있다
createBrowserRouter([
{
path: "/",
loader: () => fetchUser(),
element: <Root />,
id: "root", // 라우트에 ID를 정의
children: [
{
path: "jobs/:jobId",
loader: loadJob,
element: <JobListing />,
},
],
},
]);
//
function SomeComp() {
// "root" 라우트의 데이터를 가져와서 사용
const user = useRouteLoaderData("root");
// ...
}
자동 생성된 라우트 ID로 데이터를 저장하지만, 이 훅을 더 쉽게 사용하려면 자체적으로 라우트 ID를 제공하게 된다
Action
url을 변경하지 않고 데이터를 서버로 전송할 때 사용된다 주로 Form 컴포넌트와 함께 사용한다
function EventForm({ method, event }) {
const data = request.formData(); // Form으로 받은 입력값들에 접근하기 위한 방법이다.
function cancelHandler() {
navigate("..");
}
return (
<Form method={method} className={classes.form}>
{data && data.error && (
<ul>
{Object.values(data.error).map((err) => (
<li key={err}>{err}</li>
))}
</ul>
)}
<p>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
name="title"
required
defaultValue={event ? event.title : ""}
/>
</p>
</Form>
);
}
export default EventForm;
export async function action({ request, params }) {
const data = await request.formData();
const method = request.method;
const eventData = {
title: data.get("title"),
image: data.get("image"),
date: data.get("date"),
description: data.get("description"),
};
let url = "http://localhost:8080/events";
if (method === "PATCH") {
const someId = params.someId;
url = `${url}/${someId}`;
}
const reponse = await fetch(url, {
method: method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(eventData),
});
if (reponse.status === "422") {
return reponse;
}
if (!reponse.ok) {
throw json({ message: "Could not save event" }, { status: 500 });
}
return redirect("/events");
}
폼 양식 컴포넌트에서 모든 input요소에 name속성이 있어야 한다 => 나중에 데이터 추출할때 그것을 보고 추출한다
formData메소드를 통해 접근해야하며 get메소드를 통해 제출된 다양한 입력 필드에 접근할 수 있게된다
useSubmit
양식 내에서 값이 변경될 때 마나 양식을 제출할 수 있다, 제출할 첫 번째 인수는 다양한 값을 허용,두 번째 인수는 (대부분) 양식 제출 속성에 직접 매핑되는 옵션 세트
export async function action({ request, params }) {
const someId = params.someId;
const response = await fetch(`http://localhost:8080/events/${someId}`, {
method: request.method,
});
if (!response.ok) {
throw json(
{ message: "Could not fetch details for delete event" },
{ status: 500 }
);
}
return redirect("/events");
}
const submit = useSubmit();
function startDeleteHandler() {
const proceed = window.confirm("Are you sure?");
if (proceed) {
submit(null, { method: "delete" });// null을 해줘야 하는데 이는 현재 여기선 데이터가 필요하진 않기 때문
}
'React' 카테고리의 다른 글
Framer-motion 라이브러리(기본) (2) | 2023.12.10 |
---|---|
useContext와 mui 를 사용하여 전역에서 alret창 띄우기 (0) | 2023.07.12 |
TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'. 에러 (2) | 2023.07.06 |
React-hook-form (0) | 2023.06.18 |
리액트 인풋 에러-Warning: A component is changing an uncontrolled input to be controlled. (0) | 2023.04.26 |