
useActionData가장 최근에 실행된 action의 결과를 반환한다. action이 실행된 적 없다면 undefined를 반환한다.
import { Form, useActionData } from "react-router";
export async function action({ request }) {
const body = await request.formData();
const name = body.get("visitorsName");
return { message: `Hello, ${name}` };
}
export default function Invoices() {
const data = useActionData();
return (
<Form method="post">
<input type="text" name="visitorsName" />
{data ? data.message : "Waiting..."}
</Form>
);
}
❓ clientAction의 결과는 반환하지 않는가??
공식문서에는
clientAction은 적혀있지 않지만useLoaderData와 비슷하게action또는clientAction의 결과값을 반환할 것으로 예상된다. 확실하지 않음!
useAsyncError가장 가까운 상위 <Await> 컴포넌트에서 reject된 Promise의 에러 값을 반환한다. 즉, reject()에 인자로 전달된 값 또는 throw 키워드 뒤에 사용된 값을 반환한다.
import { Await, useAsyncError } from "react-router";
function ErrorElement() {
const error = useAsyncError();
return (
<p>Uh Oh, something went wrong! {error.message}</p>
);
}
// somewhere in your app
<Await
resolve={promiseThatRejects}
errorElement={<ErrorElement />}
/>;
useAsyncValue가장 가까운 상위 <Await> 컴포넌트에서 resolve된 Promise의 결과 값을 반환한다.
function SomeDescendant() {
const value = useAsyncValue();
// ...
}
// somewhere in your app
<Await resolve={somePromise}>
<SomeDescendant />
</Await>;
❗ 보통
<Await>컴포넌트는 render props를 사용해서 자식을 렌더링 할 수 있지만, 컴포넌트 구조가 복잡해질 수 있다.useAsyncError또는useAsyncValue를 사용하면 로직을 깔끔하게 분리할 수 있고 prop drilling도 피할 수 있다.
useBeforeUnloadwindow의 beforeunload 이벤트 발생 시 실행될 콜백함수를 설정한다. 사용자가 페이지를 떠나기 전 실행할 동작을 등록할 수 있다.
import { useBeforeUnload } from "react-router";
function MyComponent() {
const [isDirty, setIsDirty] = React.useState(false);
useBeforeUnload(
React.useCallback(() => {
if (isDirty) {
localStorage.setItem("draft", "아직 저장되지 않은 내용이 있어요!");
}
}, [isDirty])
);
return (
<input
type="text"
onChange={() => setIsDirty(true)}
placeholder="내용을 입력하세요..."
/>
);
}
params
callback: beforeunload 이벤트 발생 시 실행할 콜백함수options.capture?: true일 경우 이벤트 캡쳐링 단계에서 실행된다. (default: false)useBlockerSPA 내부에서의 navigation(페이지 이동)을 차단하고 Blocker 객체를 반환한다.. 페이지 이동 전 사용자에게 confirm 창을 보여주거나 양식을 덜 작성한 상태에서 실수로 페이지를 나가는 것을 방지할 때 사용한다.
// Boolean version
let blocker = useBlocker(value !== "");
// Function version
let blocker = useBlocker(
({ currentLocation, nextLocation, historyAction }) =>
value !== "" &&
currentLocation.pathname !== nextLocation.pathname
);
params
shouldBlock: navigation 차단 여부. boolean 또는 BlockerFunction 타입이다.❗
BlockerFunction타입interface Path { pathname: string; search: string; hash: string; } interface Location<State = any> extends Path { state: State; key: string; } declare enum Action { Pop = "POP", Push = "PUSH", Replace = "REPLACE" } type BlockerFunction = (args: { currentLocation: Location; nextLocation: Location; historyAction: HistoryAction; }) => boolean;
reutrns
Blocker 객체stateunblocked: 블로커가 대기 상태이며, 아무 navigation도 방해하지 않은 상태blocked: 블로커가 navigation을 차단한 상태proceeding: 차단되었던 navigation이 proceed() 함수 호출에 의해 다시 실행되고 있는 상태locationblocked 상태일 때: 차단된 navigation의 목적지 Locationproceeding 상태일 때: proceed() 호출 이후 실제로 이동 중인 Locationunblocked 상태일 때: undefinedproceed(): blocked 상태에서 호출하면 차단되었던 navigation을 재개reset(): blocked 상태에서 호출하면 다시 unblocked 상태로 되돌리고 현재 페이지에 그대로 있음❓
useBeforeUnload와useBlocker의 차이는?
useBeforeUnload는 사이트 자체를 나갈 때 동작하고useBlocker는 React Router 앱 안에서 이동할 때 동작한다.
useFetchernavigation을 발생시키지 않고 loader나 action을 실행한다.
import { useFetcher } from "react-router"
function SomeComponent() {
let fetcher = useFetcher()
// states are available on the fetcher
fetcher.state // "idle" | "loading" | "submitting"
fetcher.data // the data returned from the action or loader
// render a form
<fetcher.Form method="post" />
// load data
fetcher.load("/some/route")
// submit data
fetcher.submit(someFormRef, { method: "post" })
fetcher.submit(someData, {
method: "post",
encType: "application/json"
})
// reset fetcher
fetcher.reset()
}
params
options.key?: 각 fetcher에 부여하는 고유 식별자. 기본적으로는 useFetcher를 실행할 때마다 고유한 내부 인스턴스가 생성되지만, 다른 위치에서 호출하는 fetcher가 동일한 상태를 공유하게 하고 싶을 때 동일한 key를 부여하면 된다.returns
FetcherWithComponents 객체 (FetcherWithComponents 타입 상세)stateidle: 아무 작업도 하지 않는 대기 상태submitting: 폼 데이터를 전송 중인 상태loading: 데이터를 가져오거나 전송 후 재검증 중인 상태data: action 또는 loader에서 반환한 응답 데이터formData: 현재 서버로 전송되고 있는 FormData 객체formMethod: 현재 전송 중인 요청의 HTTP 메서드formAction: 현재 전송 중인 요청의 대상 URLForm: useFetcher 전용 폼 컴포넌트load: loader 실행submit: action 실행reset: fetcher의 데이터와 상태를 idle 및 undefined로 초기화❓ data가 loader 응답값인지, action의 응답값인지 어떻게 알 수 있을까?
정확하게 구분하는 속성은 없지만 유추할 수 있는 방법이 몇 가지 있다.
- 호출 함수로 구분:
fetcher.submit호출 →action,fetcher.load호출 →loaderformMethod속성으로 구분:formMethod가POST,PUT,PATCH,DELETE인 경우 →action,formMethod가 없거나GET인 경우 →loader- 데이터 구조로 구분
❗
useFetcher를 사용하여 낙관적 업데이트를 하는 방법
- 사용자 액션 발생
- state가 submitting으로 변경되고 fetcher.formData에 전송 데이터가 채워짐
- 컴포넌트에서 fetcher.formData를 확인하여 데이터가 있으면 서버 응답을 기다리지 않고 바로 화면에 반영
- 서버 작업이 완료되면 실제 서버 데이터로 렌더링된 UI가 화면을 덮어쓰며 최종 확정
import { useFetcher } from "react-router"; function LikeButton({ post }) { const fetcher = useFetcher(); // 1. 낙관적 상태 결정 // fetcher.formData가 있다는 것은 현재 전송 중임을 의미함 const isLiking = fetcher.formData?.get("intent") === "like"; // 서버 데이터보다 fetcher에 있는 '낙관적 데이터'를 우선시함 const displayLikeCount = isLiking ? post.likes + 1 : post.likes; return ( <fetcher.Form method="post" action={`/posts/${post.id}/like`}> <input type="hidden" name="intent" value="like" /> <button type="submit" disabled={isLiking}> {displayLikeCount} ❤️ {isLiking ? "(반영 중...)" : ""} </button> </fetcher.Form> ); }
useFetchers현재 활성화된 모든 fetcher 객체들의 배열을 반환한다.
import { useFetchers } from "react-router";
function SomeComponent() {
const fetchers = useFetchers();
fetchers[0].formData; // FormData
fetchers[0].state; // etc.
// ...
}
returns
Fetcher[] (Fetcher 타입 상세)useFormActionForm 객체에 전달할 action URL 경로를 계산하여 반환한다.
import { useFormAction } from "react-router";
function SomeComponent() {
// closest route URL
let action = useFormAction();
// closest route URL + "destroy"
let destroyAction = useFormAction("destroy");
}
params
action?: 현재 라우트의 URL에 붙일 action의 경로이다. action을 입력하지 않으면 useFormAction은 현재 라우트의 주소를 반환한다.options.relative?: route일 때 라우트 계층 기준으로 계산, path일 때 URL 경로 기준으로 계산한다. (default: route)useHref현재 위치를 기준으로 상대 경로를 절대 경로로 변환하여 문자열로 반환한다.
import { useHref } from "react-router";
function SomeComponent() {
let href = useHref("some/where");
// "/resolved/some/where"
}
params
to: 계산할 상대 경로options.relative?: route일 때 라우트 계층 기준으로 계산, path일 때 URL 경로 기준으로 계산한다. (default: route)useInRouterContext컴포넌트가 Router 내부에 있는지 여부를 boolean으로 반환한다.
import { useInRouterContext, useNavigate } from "react-router";
function MySharedButton() {
const inRouter = useInRouterContext();
const navigate = useNavigate(); // 이대로 라우터 밖에서 쓰면 에러 발생!
const handleClick = () => {
if (inRouter) {
// 라우터 안이라면 페이지 이동
navigate("/home");
} else {
// 라우터 밖(예: 단순 랜딩 페이지나 테스트 환경)이라면 일반 창 이동
window.location.href = "/home";
}
};
return <button onClick={handleClick}>이동</button>;
}
useLinkClickHandler<Link> 컴포넌트의 클릭 이벤트 동작을 정의하여 반환한다. 커스텀한 <Link> 컴포넌트를 구현해야 할 때 사용한다.
import { useLinkClickHandler } from "react-router";
function CustomAnchor({ to, children }) {
// 1. 클릭 핸들러를 생성합니다.
const handleClick = useLinkClickHandler(to, {
replace: false, // 히스토리를 쌓을지 여부
state: { from: "custom_button" } // 상태 전달 가능
});
return (
// 2. 일반 a 태그의 onClick에 연결합니다.
<a href={to} onClick={handleClick}>
{children}
</a>
);
}
params
to: 이동할 목적지 주소optionstarget?: _blank, _self 등 링크의 target 지정 (default: undefined)replace?: true로 설정하면 브라우저 히스토리 스택을 쌓지 않고 덮어쓰기함 (default: false)unstable_mask?: 실제 주소 대신 주소창에 표시될 URL 지정 (default: undefined)state?: 브라우저 히스토리 객체에 추가할 상태 객체 (default: undefined)preventScrollReset?: true로 설정하면 <ScrollRestoration> 컴포넌트를 사용했을 경우 스크롤 위치가 초기화되지 않도록 해줌relative?: 상대 경로를 계산할 때 라우트 계층 기준인지 URL 경로 기준인지를 결정 (default: route)viewTransition?: 페이지 전환 시 View Transition API를 적용 (default: false)unstable_defaultShouldRevalidate?: 페이지 이동 시 revalidation을 기본으로 수행할지 여부 (default: true)unstable_useTransitions?: React의 startTransition을 사용하여 navigation을 Transition으로 처리 (default: false)useLoaderData현재 라우트의 loader 또는 clientLoader의 응답값을 반환한다.
import { useLoaderData } from "react-router";
export async function loader() {
return await fakeDb.invoices.findAll();
}
export default function Invoices() {
let invoices = useLoaderData<typeof loader>();
// ...
}
❓
loader의 응답값인지clientLoader의 응답값인지 어떻게 구분할까?그 둘을 구분할 필요가 없다. 만약
loader와clientLoader가 둘 다 정의되어 있다면loader와clientLoader를 거쳐 최종적으로 결정된 결과값이useLoaderData에서 반환된다.
useLocation현재 상태의 Location 객체를 반환한다.
import * as React from 'react'
import { useLocation } from 'react-router'
function SomeComponent() {
let location = useLocation()
React.useEffect(() => {
// Google Analytics
ga('send', 'pageview')
}, [location]);
return (
// ...
);
}
useMatch전달한 pattern과 현재 URL이 매칭된다면 PathMatch 객체를 반환하고, 매칭되지 않는다면 null을 반환한다. <NavLink> 컴포넌트처럼 현재 컴포넌트가 active한 상태인지를 파악해야 할 때 유용하게 사용할 수 있다.
import { useMatch } from "react-router";
function Navbar() {
// 현재 URL이 "/users/123"이라면 매칭 성공!
// match에는 { params: { id: "123" }, pathname: "/users/123", pattern: {...} }가 담깁니다.
const match = useMatch("/users/:id");
return (
<nav>
<div style={{ color: match ? "blue" : "black" }}>
사용자 상세 정보 {match && `(ID: ${match.params.id})`}
</div>
</nav>
);
}
useMatches현재 활성화된 모든 라우트 객체들, 즉 루트부터 현재 라우트까지의 라우트 객체들을 담은 배열을 반환한다. 부모의 loaderData에 접근하거나 handle에 접근하여 브레드크럼을 만들어 사용할 때 유용하다.
import { useMatches } from "react-router";
function Breadcrumbs() {
const matches = useMatches();
return (
<ol>
{matches
// handle에 crumb 정보가 있는 라우트만 필터링
.filter((match) => Boolean(match.handle?.crumb))
.map((match, index) => (
<li key={index}>{match.handle.crumb}</li>
))}
</ol>
);
}
useNavigate페이지 이동이 가능한 함수를 반환한다.
import { useNavigate } from "react-router";
function SomeComponent() {
let navigate = useNavigate();
return (
<button onClick={() => navigate(-1)}>
Go Back
</button>
);
}
반환된 함수의 params
to: 이동할 목적지. string 경로, To 객체, 숫자 사용 가능optionsrelative?: 상대 경로를 계산할 때 라우트 계층 기준인지 URL 경로 기준인지를 결정replace?: true로 설정하면 브라우저 히스토리 스택을 쌓지 않고 덮어쓰기함state?: 브라우저 히스토리 객체에 추가할 상태 객체flushSync?: DOM 업데이트를 React의 flushSync 함수 내부에서 실행할 지 여부 (Framework, Data 모드에서만 사용 가능)preventScrollReset?: 페이지 이동 후 스크롤이 맨 위로 초기화될지 여부 (Framework, Data 모드에서만 사용 가능)viewTransition?: 페이지 이동 시 View Transition API를 사용할 지 여부 (Framework, Data 모드에서만 사용 가능)// to에 string 경로 사용
navigate("/some/route");
navigate("/some/route?search=param");
// to에 To 객체 사용
navigate({
pathname: "/some/route",
search: "?search=param",
hash: "#hash",
state: { some: "state" },
});
// to에 숫자 사용
navigate(-1); // 뒤로가기
navigate(1); // 앞으로가기
useNavigate에서 반환되는 함수가 새로 생성된다.useNavigate에서 반환되는 함수의 리턴 타입은 void이다.useNavigate에서 반환되는 함수가 새로 생성되지 않고 메모리 주소가 유지된다.useNavigate에서 반환되는 함수의 리턴 타입은 Promise<void>이다.따라서 다음과 같은 문제가 발생할 수 있다.
@typescript-eslnt/no-floating-promises에서 에러가 발생한다.React.use(navigate())처럼 쓰려고 할 때 Argument of type 'void | Promise<void>' is not assignable to parameter of type 'Usable<void>’ 에러가 발생한다.해결 방법: 지금 사용하고 있는 모드에 따라 타입 확장하기
// Declarative mode
declare module "react-router" {
interface NavigateFunction {
(to: To, options?: NavigateOptions): void;
(delta: number): void;
}
}
// Data or Framework mode
declare module "react-router" {
interface NavigateFunction {
(to: To, options?: NavigateOptions): Promise<void>;
(delta: number): Promise<void>;
}
}
useNavigation현재 상태의 Navigation 객체를 반환한다. navigation.state를 사용하여 로딩 UI를 구현하거나 폼 제출 중 navigation.formData를 읽을 수도 있다.
import { useNavigation } from "react-router";
function RootLayout() {
const navigation = useNavigation();
// 현재 페이지 이동 중인지 확인
const isPageLoading = navigation.state === "loading";
return (
<div style={{ opacity: isPageLoading ? 0.5 : 1 }}>
{isPageLoading && <div className="spinner">로딩 중...</div>}
<nav>...</nav>
<main>
<Outlet />
</main>
</div>
);
}
useNavigationType어떤 방법을 통해 현재 페이지에 도달했는지를 알려주는 NavigationType(”POP”, “PUSH”, “REPLACE”)의 문자열을 반환한다.
import { useNavigationType } from "react-router";
function MyComponent() {
const navType = useNavigationType();
// 사용자가 뒤로 가기로 왔는지, 링크를 눌러서 왔는지에 따라 다른 처리가 가능합니다.
if (navType === "POP") {
console.log("사용자가 뒤로 가기나 새로고침으로 이 페이지에 왔습니다.");
}
return <div>현재 접속 방식: {navType}</div>;
}
useOutlet자식 라우트의 React Element를 반환한다. <Outlet> 컴포넌트의 훅 버전이라고 생각하면 된다. 복잡한 애니메이션이나 조건부 레이아웃을 처리할 때 사용한다.
import { useOutlet } from "react-router";
function Layout() {
const outlet = useOutlet();
return (
<div>
<header>헤더</header>
{/* outlet 변수 자체가 리액트 요소이므로 바로 렌더링 가능 */}
<div className="animation-wrapper">
{outlet}
</div>
</div>
);
}
params
context?: outlet에 전달할 컨텍스트useOutletContext부모 라우트에서 outlet에 전달한 context를 반환한다.
// Parent route
function Parent() {
const [count, setCount] = React.useState(0);
return <Outlet context={[count, setCount]} />;
}
// Child route
import { useOutletContext } from "react-router";
function Child() {
const [count, setCount] = useOutletContext();
const increment = () => setCount((c) => c + 1);
return <button onClick={increment}>{count}</button>;
}
타입스크립트를 사용 중이라면 useOutletContext<ContextType>()을 호출하는 대신 부모 라우트에서 커스텀 훅을 만들어 제공하는 것이 좋다.
// Parent route
export function useUser() {
return useOutletContext<ContextType>();
}
// Child route
function Child() {
const { user } = useUser();
}
useParams현재 URL 경로의 동적 파라미터 정보를 key/value의 객체 형태로 반환한다.
// /posts/:postId/comments/:commentId
import { useParams } from "react-router";
export default function Post() {
let params = useParams();
return (
<h1>
Post: {params.postId}, Comment: {params.commentId}
</h1>
);
}
// /files/*
import { useParams } from "react-router";
export default function File() {
let params = useParams();
let catchall = params["*"];
}
export default function File() {
let { "*": catchall } = useParams();
console.log(catchall);
}
unstable_usePromptuseBlocker 훅을 래핑하여 window.confirm 창을 사용자에게 보여주는 훅이다.
⚠️ confirm 창이 열려 있을 때 뒤로가기 또는 앞으로 가기를 눌렀을 경우의 동작이 브라우저마다 제각각이거나 오작동할 확률이 높기 때문에
unstable_플래그가 영구적으로 유지될 예정이다.
unstable_usePrompt({
message: "Are you sure?",
when: ({ currentLocation, nextLocation }) =>
value !== "" &&
currentLocation.pathname !== nextLocation.pathname,
});
useResolvedPath현재 위치를 기준으로 상대 경로를 절대 경로로 변환하여 반환한다. useHref와 유사하게 동작하지만 문자열이 아니라 Path 객체를 반환한다는 점이 다르다.
import { useResolvedPath } from "react-router";
function SomeComponent() {
// if the user is at /dashboard/profile
let path = useResolvedPath("../accounts");
path.pathname; // "/dashboard/accounts"
path.search; // ""
path.hash; // ""
}
useRevalidator수동으로 데이터의 revalidation을 실행한다. window focus, polling(채팅 알림, 주식 시세처럼 주기적으로 호출) 같은 경우에 사용하고 일반적인 CRUD 작업에는 useRevalidator를 사용하지 않는 것이 좋다.
import { useRevalidator } from "react-router";
function WindowFocusRevalidator() {
const revalidator = useRevalidator();
useFakeWindowFocus(() => {
revalidator.revalidate();
});
return (
<div hidden={revalidator.state === "idle"}>
Revalidating...
</div>
);
}
returns
revalidate: 재검증 실행 함수state: 현재 재검증 상태useRouteErrorroute module의 ErrorBoundary 내부에서 action, loader, 컴포넌트 렌더링 중 발생한 에러에 접근한다.
export function ErrorBoundary() {
const error = useRouteError();
return <div>{error.message}</div>;
}
useRouteLoaderData특정 라우트의 ID를 사용하여 해당 라우트의 loaderData에 접근한다.
import { useRouteLoaderData } from "react-router";
function SomeComponent() {
const { user } = useRouteLoaderData("root");
}
// You can also specify your own route ID's manually in your routes.ts file:
route("/", "containers/app.tsx", { id: "app" })
useRouteLoaderData("app");
useRoutes<Routes> 컴포넌트의 훅 버전으로, route tree로 사용할 수 있는 React element를 생성한다.
import { useRoutes } from "react-router";
function App() {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
children: [
{
path: "messages",
element: <DashboardMessages />,
},
{ path: "tasks", element: <DashboardTasks /> },
],
},
{ path: "team", element: <AboutPage /> },
]);
return element;
}
params
routes: 라우트 계층 구조를 정의한 RouteObject 배열locationArg?: 현재 Location 대신 사용할 Location 객체 또는 pathnameuseSearchParams현재 URL의 URLSearchParams와 이를 업데이트하는 함수를 배열로 반환한다. 즉, [searchParams, setSearchParams]를 반환한다.
let [searchParams, setSearchParams] = useSearchParams();
// a search param string
setSearchParams("?tab=1");
// a shorthand object
setSearchParams({ tab: "1" });
// object keys can be arrays for multiple values on the key
setSearchParams({ brand: ["nike", "reebok"] });
// an array of tuples
setSearchParams([["tab", "1"]]);
// a `URLSearchParams` object
setSearchParams(new URLSearchParams("?tab=1"));
// function callback update
setSearchParams((searchParams) => {
searchParams.set("tab", "2");
return searchParams;
});
params
defaultInit?: 초기값. 하지만 첫 렌더링 시에는 URL에 반영되지 않는다.⚠️
useState의 함수형 업데이트는 여러 번 호출 시 이전 호출의 결과를 기다려주는 queueing 로직이 적용되어 있지만setSearchParams의 함수형 업데이트는 그렇지 않다. 이 동작을 구현하기 위해서는setState를 추가로 사용하거나 한 번의 호출 안에 모든 업데이트를 넣어야 한다.// ❌ 잘못된 방법 (각각 호출) setSearchParams({ a: 1 }); setSearchParams({ b: 2 }); // ✅ 올바른 방법 (한 번에 호출) setSearchParams(prev => { const next = new URLSearchParams(prev); next.set("a", "1"); next.set("b", "2"); return next; }); // ✅ 올바른 방법 (useState 사용) const [searchParams, setSearchParams] = useSearchParams(); const [localParams, setLocalParams] = useState( () => new URLSearchParams(searchParams) ); useEffect(() => { setSearchParams(localParams, { replace: true }); }, [localParams, setSearchParams]);
⚠️
searchParams는 Stable Reference이므로, 컴포넌트가 리렌더링되어도 메모리 주소가 변경되지 않는다. 따라서useEffect의 의존성 배열에 넣어도 안전하게 사용할 수 있다. 하지만searchParams내부의 값은.set()이나.append()를 사용하면 변경될 수 있기 때문에 이렇게 변경한다면 데이터는 변경되지만 URL은 변경되지 않아 문제가 발생할 수 있다. 따라서 제대로 사용하기 위해서는URLSearchParams객체 자체를 업데이트해야 한다.// ❌ 위험한 코드 (Mutable 수정) const handleClick = () => { // 1. 객체를 직접 수정 (주소창은 안 바뀜!) searchParams.set("tab", "2"); // 2. 다른 상태를 바꿈 -> 리렌더링 유발 setSomeOtherState(prev => prev + 1); // 결과: URL은 그대로인데 화면은 탭 2로 바뀔 수 있음 (데이터 불일치 발생) }; // ✅ 권장되는 코드 (Immutable 방식) const handleClick = () => { const newParams = new URLSearchParams(searchParams); newParams.set("tab", "2"); setSearchParams(newParams); // 주소창과 리액트 상태를 동시에 업데이트! };
useSubmit수동으로 Form을 제출한다.
import { useSubmit } from "react-router";
function SomeComponent() {
const submit = useSubmit();
return (
<Form onChange={(event) => submit(event.currentTarget)} />
);
}
useViewTransitionState현재 페이지에서 특정 페이지로 활성화된 View Transition이 있으면 true를 반환한다. <Link>, <Form>, useNavigate, useSubmit에서 viewTransition 옵션이 활성화되어있어야 제대로 동작한다.
import { useViewTransitionState } from "react-router";
function ImageItem({ id, src }) {
// "/photos/1" 경로로 뷰 트랜지션이 진행 중인지 확인
const isTransitioning = useViewTransitionState(`/photos/${id}`);
return (
<div
style={{
// 트랜지션 중일 때만 특별한 view-transition-name을 부여
viewTransitionName: isTransitioning ? "selected-photo" : "none",
opacity: isTransitioning ? 0.5 : 1,
}}
>
<img src={src} alt="Photo" />
</div>
);
}
params
to: 이동할 목적지 주소options.relative?: 경로 계산 기준이 route인지 path인지 결정