티스토리 뷰
<Outlet>
상위 경로와 일치하는 하위 경로를 렌더링한다.
import { Outlet } from "@remix-run/react";
export default function SomeParent() {
return (
<div>
<h1>Parent Content</h1>
<Outlet />
</div>
);
}
Props
context
outlet 아래의 엘리먼트 트리에 컨텍스트 값을 전달한다. 상위 경로가 하위 경로에 값을 제공해야 할 때 사용한다.
<Outlet context={myContextValue} />
하위 컴포넌트에서 전달받은 컨텍스트를 가져올 때는 useOutletContext를 사용한다.
<Await>
useLoaderData에서 액세스한 지연된 로더 promise를 해결하는 역할을 한다.
import { Await } from "@remix-run/react";
<Suspense fallback={<div>Loading...</div>}>
<Await resolve={somePromise}>
{(resolvedValue) => <p>{resolvedValue}</p>}
</Await>
</Suspense>;
Props
resolve
데이터가 스트리밍되는 시점을 해결하기 위해 useLoaderData에서 promise를 가져온다.
<Await resolve={somePromise} />
promise가 완료되지 않으면, 상위 suspense 바운더리의 대체가 렌더링된다.
<Suspense fallback={<div>Loading...</div>}>
<Await resolve={somePromise} />
</Suspense>
promise가 완료되면, children이 렌더링된다.
children
children은 콜백 또는 React 엘리먼트가 렌더링될 수 있다.
<Await resolve={somePromise}>
{(resolvedValue) => <p>{resolvedValue}</p>}
</Await>
children props가 React 엘리먼트라면, 성공 값은 하위 트리에 useAsyncValue를 통해 접근할 수 있다.
<Await resolve={somePromise}>
<SomeChild />
</Await>
// 하위 트리
import { useAsyncValue } from "@remix-run/react";
function SomeChild() {
const value = useAsyncValue();
return <p>{value}</p>;
}
errorElement
promise가 실패할 때, 에러 바운더리를 렌더할 수 있다.
<Await errorElement={<div>Oops!</div>} />
에러는 하위 트리에서 useAsyncError를 통해 접근할 수 있다.
<Await errorElement={<SomeChild />} />
// 하위 컴포넌트
import { useAsyncError } from "@remix-run/react";
function SomeChild() {
const error = useAsyncError();
return <p>{error.message}</p>;
}
<Form>
fetch를 통해 action에 데이터를 제출하고 기본 HTML 폼 이상의 향상된 유저 인터페이스를 활성화하는 useNavigation에서 보류 상태를 활성화하는 점진적으로 향상된 HTML <form>이다.
폼의 action이 완료되면, 페이지의 모든 데이터가 서버에서 자동으로 재검증되어 UI와 데이터의 동기화가 유지된다.
HTML 폼 API를 사용하기 때문에, 서버에서 렌더링된 페이지는 JavaScript가 로드되기 전에 기본 수준에서 상호작용한다.
Remix가 제출물을 관리하는 대신, 브라우저는 제출물은 물론 보류 상태(회전하는 파비콘 등)도 관리한다. JavaScript가 로드된 후 Remix가 웹 애플리케이션 사용자 경험을 활성화한다.
Formdms URL도 변경해야 하거나 브라우저 기록 스택에 항목을 추가해야 하는 제출에 가장 유용하다.
브라우저 기록 스택을 조작해서는 안되는 폼의 경우 <fetcher.Form>을 사용하세요.
import { Form } from "@remix-run/react";
function NewEvent() {
return (
<Form action="/events" method="post">
<input name="title" type="text" />
<input name="description" type="text" />
</Form>
);
}
Props
action
폼 데이터를 제출할 URL
undefined일 경우, 컨텍스트에서 가장 가까운 경로가 기본값이다.
상위 경로가 <Form>을 렌더링하지만 URL이 더 깊은 하위 경로와 일치하는 경우 폼은 상위 경로에 게시된다.
마찬가지로 하위 경로 내부의 내부의 폼은 하위 경로에 게시된다. 이는 항상 전체 URL을 가리키는 기존의 <form>과는 다르다.
method
사용할 HTTP 동사를 결정한다.
DELETE, GET, PATCH, POST, PUT. 기본값은 GET
<Form method="post" />
기본 <form>은 GET 및 POST 만 지원하므로 점진적인 향상을 지원하려면 다른 동사를 피해야한다.
encType
폼 제출에 사용할 인코딩 유형
<Form encType="multipart/form-data" />
기본값은 application/x-www-form-urlencoded. 파일 업로드에는 multipart/form-data를 사용한다.
navigate
<Form navigate={false} >를 지정하여 탐색을 건너뛰고 내부적으로 fetcher를 사용하도록 폼에 지시할 수 있다.
이는 본질적으로 결과 데이터에 신경쓰지 않고 제출을 시작하고 useFetchers( )를 통해 보류 상태에 액세스하려는 useFetcher( ) + <fetcher.Form>의 약어이다.
<Form method="post" navigate={false} />
fetcherKey
비탐색 Form을 사용할 때, 선택적으로 사용할 자체 fetcher key를 지정할 수 있다.
<Form method="post" navigate={false} fetcherKey="my-key" />
preventScrollReset
<ScrollRestoration> 을 사용하는 경우, 폼을 제출할 때 스크롤 위치가 창 상단으로 재설정되는 것을 방지할 수 있다.
<Form preventScrollReset />
replace
새 항목을 푸시하는 대신 기록 스택의 현재 항목을 바꾼다.
<Form replace />
reloadDocument
true인 경우 클라이언트 측 라우팅 대신 브라우저를 사용하여 폼을 제출한다.
기존의 <form>과 동일하다.
<Form reloadDocument />
<form>이상으로 권장된다. action prop이 생략되면, <Form>과 <form>은 현재 URL이 무엇인지에 따라 다른 action을 호출하는 경우가 있다. <form>은 현재 URL을 기본값으로 사용하지만, <Form>은 폼이 렌더링되는 라우트의 URL을 사용하기 때문이다.
unstable_viewTransition
document.startViewTransition( )에서 최종 상태 업데이트를 래핑하여 탐색에 대한 View Transition을 활성화한다.
뷰 전환에 특정 스타일을 적용해야 하는 경우 unstable_useViewTransitionState( )도 활용해야 한다.
✏️ NOTE
?index
index 경로와 해당 상위 경로는 동일한 URL을 공유하므로 이를 구별하는 데 ?index 매개변수가 사용된다.
<Form action="/accounts?index" method="post" />
action URL | route action |
/accounts?index | app/routes/accounts._index.tsx |
/accounts | app/routes/accounts.tsx |
<Link>
클라이언트 측 라우팅으로 탐색을 활성화하는 <a href> 랩퍼이다.
import { Link } from "@remix-run/react";
<Link to="/dashboard">Dashboard</Link>;
Props
to: string
가장 기본적인 사용법
<Link to="/some/path" />
to: Partial<Path>
Partial<Path> 값을 전달할 수도 있다.
<Link
to={{
pathname: "/some/path",
search: "?query=string",
hash: "#hash",
}}
/>
prefetch
링크에 대한 데이터 및 모듈 프리패치 동작을 정의한다.
<>
<Link /> {/* defaults to "none" */}
<Link prefetch="none" />
<Link prefetch="intent" />
<Link prefetch="render" />
<Link prefetch="viewport" />
</>
- none - 기본값. 프리패칭 없음
- intent - 유저가 링크에 마우스를 올리거나 포커스를 두면 프리패치
- render - 링크가 렌더링될 때 프리패치
- viewport - 링크가 뷰포트에 있을 때 프리패치. 모바일에 매우 유용함
프리패칭은 HTML <link rel="prefetch"> 태그를 사용해 수행된다. 태그들은 link 뒤에 삽입된다.
<nav>
<a href="..." />
<a href="..." />
<link rel="prefetch" /> {/* might conditionally render */}
</nav>
때문에 nav: last-child를 사용한다면, 스타일이 조건부로 마지막 링크(및 기타 유사한 셀렉터)에서 떨어지지 않도록 nav: last-of-type을 사용해야 한다.
preventScrollReset
<ScrollRestoration>을 사용하는 경우 링크를 클릭할 때 스크롤 위치가 창 상단으로 재설정되는 것을 방지할 수 있다.
<Link to="?tab=one" preventScrollReset />
유저가 뒤로/앞으로 버튼이 있는 위치로 돌아올 때 스크롤 위치가 복원되는 것을 방지하지 않으며, 유저가 링크를 클릭할 때 재설정되는 것을 방지할 뿐이다.
relative
링크의 상대 경로 동작을 정의한다.
<Link to=".." />; // default: "route"
<Link relative="route" />;
<Link relative="path" />;
- route - 기본값. 경로 계층 구조에 상대적이므로 ".."는 현재 경로 패턴의 모든 URL 세그먼트를 제거함
- path - 경로에 상대적이므로 ".."는 하나의 URL 세그먼트를 제거함
reloadDocument
링크를 클릭할 때 클라이언트 측 라우팅 대신 문서 탐색을 사용하고, 브라우저는 전환을 정상적으로 처리한다.(마치 <a href>가 있던 것처럼)
<Link to="/logout" reloadDocument />
replace
새 항목을 푸시하는 대신 기록 스택의 현재 항목을 대체한다.
<Link replace />
# 기록스택이 다음과 같다면
A -> B
# 보통의 링크 클릭은 새 항목을 추가함
A -> B -> C
# 하지만 replace는 B가 C로 대체됨
A -> C
state
영구적인 클라이언트 측 라우팅 상태를 그 다음 위치에 추가한다.
<Link to="/somewhere/else" state={{ some: "value" }} />
위치 상태는 location에서 액세스된다.
function SomeComp() {
const location = useLocation();
location.state; // { some: "value" }
}
이 상태는 history.state 위에 구현되므로 서버에서 접근할 수 없다.
unstable_viewTransition
document.startViewTransition( )에서 최종 상태 업데이트를 래핑하여 탐색에 대한 View Transition을 활성화한다.
<Link to={to} unstable_viewTransition>
Click me
</Link>
View Transition에 특정 스타일을 적용해야 하는 경우 unstable_useViewTransitionState( )도 활용해야 한다.
function ImageLink(to) {
const isTransitioning =
unstable_useViewTransitionState(to);
return (
<Link to={to} unstable_viewTransition>
<p
style={{
viewTransitionName: isTransitioning
? "image-title"
: "",
}}
>
Image Number {idx}
</p>
<img
src={src}
alt={`Img ${idx}`}
style={{
viewTransitionName: isTransitioning
? "image-expand"
: "",
}}
/>
</Link>
);
}
<Links />
라우트 모듈 링크 내보내기로 생성된 모든 <link> 태그를 렌더링한다.
일반적으로 app/root.tsx에 있는 HTML의 <head> 내부에 렌더링해야 한다.
// app/root.tsx
import { Links } from "@remix-run/react";
export default function Root() {
return (
<html>
<head>
<Links />
</head>
<body></body>
</html>
);
}
<NavLink>
active 및 pending 상태 스타일을 지정하기 위한 추가 props로 <Link>를 감싼다.
import { NavLink } from "@remix-run/react";
<NavLink
to="/messages"
className={({ isActive, isPending }) =>
isPending ? "pending" : isActive ? "active" : ""
}
>
Messages
</NavLink>;
자동 속성
.active
active 클래스가 활성화되면 <NavLink>에 추가되므로 CSS를 사용해 스타일링을 할 수 있다.
<NavLink to="/messages" />
a.active {
color: red;
}
aria-current
NavLink가 활성화되면 기본 앵커 태그에 <a aria-current="page"> 가 자동으로 적용된다.
.pending
pending 클래스가 탐색 중에 보류 중이라면 <NavLink>에 추가되므로 CSS를 사용해 스타일링할 수 있다.
<NavLink to="/messages" />
a.pending {
color: red;
}
.transitioning
탐색 중에 <NavLink unstable_viewTransition>컴포넌트가 전환될 때 transitioning 클래스가 컴포넌트에 추가되므로 CSS를 사용해 스타일링할 수 있다.
<NavLink to="/messages" unstable_viewTransition />
a.transitioning {
view-transition-name: my-transition;
}
Props
className 콜백
적용된 클래스 이름을 사용자화할 수 있도록 active 및 pending 상태로 콜백
<NavLink
to="/messages"
className={({ isActive, isPending }) =>
isPending ? "pending" : isActive ? "active" : ""
}
>
Messages
</NavLink>
style 콜백
적용된 스타일을 사용자화할 수 있도록 active 및 pending 상태로 콜백
<NavLink
to="/messages"
style={({ isActive, isPending }) => {
return {
fontWeight: isActive ? "bold" : "",
color: isPending ? "red" : "black",
};
}}
>
Messages
</NavLink>
children 콜백
<NavLink>의 콘텐츠를 사용자화할 수 있도록 active 및 pending 상태로 콜백
<NavLink to="/tasks">
{({ isActive, isPending }) => (
<span className={isActive ? "active" : ""}>Tasks</span>
)}
</NavLink>
end
NavLink의 to 경로에만 일치하도록 active와 pending 상태에 대한 일치 논리를 변경한다.
to보다 URL이 더 긴 경우에는 더 이상 active로 간주되지 않는다.
Link | URL | isActive |
<NavLink to="/tasks" /> | /tasks | true |
<NavLink to="/tasks /> | /tasks/123 | true |
<NavLink to="/tasks" end /> | /tasks | true |
<NavLink to="/tasks" end /> | /tasks/123 | false <- URL이 to 경로보다 김 |
모든 URL이 /와 일치하기 때문에 <NavLink to="/" >는 예외적인 경우이다.
기본적으로 모든 단일 경로와 일치하는 것을 피하기 위해 end prop을 효과적으로 무시하고 루트 경로에 있을 때만 일치한다.
caseSensitive
caseSensitive를 추가하면 일치 논리가 대소문자를 구분하도록 변경한다.
Link | URL | isActive |
<NavLink to="/SpOnGe-b0B" /> | /sponge-bob | true |
<NavLink to="/SpOnGe-boB" caseSeneitive /> | /sponge-bob | false |
unstable_viewTransition
document.startViewTransition( )에서의 최종 상태 업데이트를 래핑하여 탐색에 대한 View Transition을 활성화한다.
기본적으로 전환 중에, 보기 전환을 사용자화하는 데 사용할 수 있는 transitioning 클래스가 <a> 엘리먼트에 추가된다.
a.transitioning p {
view-transition-name: "image-title";
}
a.transitioning img {
view-transition-name: "image-expand";
}
<NavLink to={to} unstable_viewTransition>
<p>Image Number {idx}</p>
<img src={src} alt={`Img ${idx}`} />
</NavLink>
isTransitioning 값에 따라 추가로 사용자화하기 위해 className/style props 또는 children에 전달된 렌더링 props를 사용할 수도 있다.
<NavLink to={to} unstable_viewTransition>
{({ isTransitioning }) => (
<>
<p
style={{
viewTransitionName: isTransitioning
? "image-title"
: "",
}}
>
Image Number {idx}
</p>
<img
src={src}
alt={`Img ${idx}`}
style={{
viewTransitionName: isTransitioning
? "image-expand"
: "",
}}
/>
</>
)}
</NavLink>
<PrefetchPageLinks />
페이지의 모든 assets를 프리패치하여 해당 페이지를 즉시 탐색할 수 있다.
이는 특정 페이지의 모든 assets(데이터, 모듈, CSS)에 대해 <link rel="prefetch"> 및 <link rel="modulepreload" /> 태그를 렌더링하여 수행한다.
<Link rel="prefetch">는 이를 내부적으로 사용하지만 다른 이유로 페이지를 프리페치하도록 렌더링할 수 있다.
<PrefetchPageLinks page="/absolute/path/to/your-path" />
단, 절대 경로를 사용해야 한다.
<LiveReload />
앱을 Remix asset 서버에 연결하고 개발 중에 파일이 변경되면 자동으로 페이지를 다시 로드한다.
프로덕션에서는 null을 렌더링하므로 항상 루트 경로에서 안전하게 렌더링할 수 있다.
// app/root.tsx
import { LiveReload } from "@remix-run/react";
export default function Root() {
return (
<html>
<head />
<body>
<LiveReload />
</body>
</html>
);
}
Props
origin
Live Reload 프로토콜의 사용자 정의 원본을 지정한다.
제공된 URL은 http 프로토콜을 사용해야 하며 내부적으로 ws 프로토콜로 업그레이드 된다. 이는 Remix 개발 서버 앞에서 역방향 프록시를 사용할 때 유용하다.
기본값은 REMIX_DEV_ORIGIN 환경 변수이거나 REMIX_DEV_ORIGIN이 설정되지 않은 경우에만 window.location.origin이다.
port
Live Reload 프로토콜에 대한 사용자 정의 포트를 지정한다.
기본값은 REMIX_DEV_ORIGIN 환경 변수에서 파생된 포트이거나 REMIX_DEV_ORIGIN이 설정되지 않은 경우에만 8002이다.
timeoutMs
Live Reload 프로토콜에 대한 사용자 지정 시간 초과를 밀리초 단위로 지정할 수 있다.
웹 소켓 연결이 끊어진 경우 다시 연결을 시도하기 전의 지연 시간이다.
기본값은 1000이다.
<Meta />
라우트 모듈 meta 내보내기에 의해 생성된 모든 <meta> 태그를 렌더링한다.
일반적으로 app/root.tsx에 있는 HTML의 <head> 내부에 렌더링해야 한다.
// app/root.tsx
import { Meta } from "@remix-run/react";
export default function Root() {
return (
<html>
<head>
<Meta />
</head>
<body></body>
</html>
);
}
<Scripts />
앱의 클라이언트 런타임을 렌더링한다.
일반적으로 app/root.tsx에 있는 HTML의 <body> 내부에서 렌더링해야 한다.
// app/root.tsx
import { Scripts } from "@remix-run/react";
export default function Root() {
return (
<html>
<head />
<body>
<Scripts />
</body>
</html>
);
}
<Scripts /> 컴포넌트를 렌더링하지 않는다면, 앱은 HTML 및 브라우저 동작에만 의존하여 JavaScript 없이 기존 웹 앱처럼 계속 동작한다.
Props
<Scripts> 컴포넌트는 특정 속성을 통해 기본 <script>태그로 전달할 수 있다.
- <Scripts crossOrigin > - 앱과 다른 서버에서 정적 assets를 호스팅하기 위함
- <Scripts nonce > - <script>태그에 대한 nonce-sources가 있는 스크립트에 대한 콘텐츠 보안 정책을 지원
async / defer / src / type / noModule 등의 속성은 Remix 내부에서 관리되기 때문에 전달할 수 없다.
<ScrollRestoration>
loader가 완료된 후 위치 변경 시 브라우저의 스크롤 복원을 에뮬레이트한다.
이렇게 하면 여러 도메인에서도 스크롤 위치가 적시에 올바른 지점으로 복원된다.
<Scripts /> 컴포넌트 바로 앞에 이들 중 하나만 렌더링해야 한다.
import {
Scripts,
ScrollRestoration,
} from "@remix-run/react";
export default function Root() {
return (
<html>
<body>
{/* ... */}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
Props
getKey
선택 항목. 스크롤 위치를 복원하는 데 사용되는 키를 정의한다.
<ScrollRestoration
getKey={(location, matches) => {
// default behavior
return location.key;
}}
/>
nonce
<ScrollRestoration>은 스크롤 깜빡임을 방지하기 위해 인라인 <script>를 렌더링한다.
nonce prop은 CSP nonce 사용을 허용하기 위해 script 태그로 전달된다.
<ScrollRestoration nonce={cspNonce} />
Preventing Scroll Reset
새 위치로 이동하면 스크롤 위치가 페이지 상단으로 재설정된다.
링크와 폼에서 "맨 위로 스크롤" 동작을 방지할 수 있다.
<Link preventScrollReset={true} />;
<Form preventScrollReset={true} />;
'코딩 > 코딩노트' 카테고리의 다른 글
백엔드에서 API를 아직 만들지 않았다면 MSW를 사용하자! - 회원가입 (0) | 2024.05.30 |
---|---|
[Remix]Remix 공식문서 파헤치기 7탄 - 튜토리얼 진행하기 (0) | 2024.04.29 |
[Remix]Remix 공식문서 파헤치기 5탄 - Hooks (0) | 2024.04.23 |
[Remix]Remix 공식문서 파헤치기 4탄 - 파일 컨벤션 (0) | 2024.04.21 |
[Remix]Remix 공식문서 파헤치기 3탄 - Form vs fetcher (0) | 2024.04.19 |