<ViewTransition>
을 사용하면 Transition 내부에서 업데이트되는 엘리먼트에 애니메이션을 적용할 수 있습니다.
import {unstable_ViewTransition as ViewTransition} from 'react';
<ViewTransition>
<div>...</div>
</ViewTransition>
레퍼런스
<ViewTransition>
엘리먼트를 <ViewTransition>
으로 감싸면 Transition 내부에서 업데이트할 때 애니메이션을 적용할 수 있습니다. React는 다음 휴리스틱을 사용하여 View Transition이 애니메이션에 활성화되는지 판단합니다.
enter
: 해당 Transition에서ViewTransition
자체가 삽입되면 활성화됩니다.exit
: 해당 Transition에서ViewTransition
자체가 삭제되면 활성화됩니다.update
:ViewTransition
내부에서 React가 수행하는 DOM 변경(예: 프로퍼티 변경)이 있거나 인접한 형제 엘리먼트의 영향으로ViewTransition
경계 자체의 크기나 위치가 변경되는 경우 활성화됩니다. 중첩된ViewTransition
이 있으면 변경이 부모가 아닌 해당 항목에 적용됩니다.share
: 이름이 지정된ViewTransition
이 삭제된 서브트리 내부에 있고 같은 이름을 가진 다른 이름 있는ViewTransition
이 같은 Transition에서 삽입된 서브트리의 일부인 경우 공유 엘리먼트 Transition을 형성하며, 삭제된 것에서 삽입된 것으로 애니메이션됩니다.
기본적으로 <ViewTransition>
은 부드러운 크로스 페이드(브라우저 기본 View Transition)로 애니메이션됩니다. <ViewTransition>
컴포넌트에 View Transition 클래스를 제공하여 애니메이션을 커스터마이징할 수 있습니다. 각 트리거 유형에 대해 애니메이션을 커스터마이징할 수 있습니다(View Transition 스타일링 참고).
자세히 살펴보기
내부적으로 React는 <ViewTransition>
컴포넌트 내부에 중첩된 가장 가까운 DOM 노드의 인라인 스타일에 view-transition-name
을 적용합니다. <ViewTransition><div /><div /></ViewTransition>
처럼 여러 형제 DOM 노드가 있을 경우, React는 각 노드의 이름이 고유하도록 접미사를 추가하지만, 개념적으로는 동일한 전환에 속하는 것으로 간주합니다.
React는 내부적으로 startViewTransition
을 자체적으로 호출하므로 직접 호출해서는 안됩니다. 실제로 페이지에서 다른 스크립트나 코드가 ViewTransition을 실행하고 있다면 React가 이를 중단합니다. 따라서 React 자체를 사용하여 이를 조정하는 것을 권장합니다. 과거에 ViewTransition을 트리거하는 다른 방법이 있었다면 내장 방법으로 마이그레이션하는 것을 권장합니다.
다른 React ViewTransition이 이미 실행 중이라면, React는 그것들을 완료할 때까지 다음 전환을 시작하지 않습니다. 그러나 중요한 점은 첫 번째 전환이 진행되는 동안 여러 업데이트가 발생하면, 그 업데이트들은 모두 하나로 묶여 처리된다는 것입니다. 예를 들어 A에서 B로 이동하는 전환을 시작했다고 가정합시다. 그 사이에 C로 가는 업데이트가 발생하고 다시 D로 가는 업데이트가 발생한다면, 첫 번째 A->B 애니메이션이 끝난 후 다음 애니메이션은 B에서 D로 전환됩니다.
getSnapshotBeforeUpdate
생명주기는 startViewTransition
전에 호출되고 일부 view-transition-name
은 동시에 업데이트됩니다.
그런 다음 React는 startViewTransition
을 호출합니다. updateCallback
내부에서 React는 다음을 수행합니다.
- DOM에 변경을 적용하고
useInsertionEffect
를 호출합니다. - 폰트가 로드될 때까지 기다립니다.
- componentDidMount, componentDidUpdate, useLayoutEffect, refs를 호출합니다.
- 대기 중인 탐색이 완료될 때까지 기다립니다.
- 그런 다음 React는 레이아웃의 변경 사항을 측정하여 어떤 경계가 애니메이션되어야 하는지 확인합니다.
startViewTransition
의 ready Promise가 해결된 이후, React는 view-transition-name
을 되돌립니다. 그 다음 React는 onEnter
, onExit
, onUpdate
, onShare
콜백들을 호출하여 애니메이션에 대해 수동으로 프로그래밍 방식의 제어를 할 수 있도록 합니다. 이 호출은 내장된 기본 애니메이션이 이미 계산된 이후에 이루어집니다.
이 시퀀스 중간에 flushSync
가 발생하면 동기적으로 완료되어야 하는 특성 때문에 React는 해당 Transition을 건너뜁니다.
startViewTransition
의 finished Promise가 해결된 이후에 React는 useEffect
를 호출합니다. 이렇게 하면 useEffect
가 애니메이션 성능에 영향을 주지 않도록 방지할 수 있습니다. 그러나 이것이 반드시 보장되는 것은 아닙니다. 만약 애니메이션이 실행되는 도중에 다른 setState
가 발생하면, 순차적 동작 보장을 유지하기 위해 useEffect
를 더 일찍 호출해야 할 수도 있습니다.
Props
기본적으로 <ViewTransition>
은 부드러운 크로스 페이드로 애니메이션됩니다. 이러한 프로퍼티로 애니메이션을 커스터마이즈하거나 공유 엘리먼트 Transition을 지정할 수 있습니다.
- optional
enter
: 문자열 또는 객체. “enter”가 활성화될 때 적용할 View Transition 클래스입니다. - optional
exit
: 문자열 또는 객체. “exit”이 활성화될 때 적용할 View Transition 클래스입니다. - optional
update
: 문자열 또는 객체. “update”가 활성화될 때 적용할 View Transition 클래스입니다. - optional
share
: 문자열 또는 객체. 공유 엘리먼트가 활성화될 때 적용할 View Transition 클래스입니다. - optional
default
: 문자열 또는 객체. 다른 일치하는 활성화 프로퍼티가 없을 때 사용되는 View Transition 클래스입니다. - optional
name
: 문자열 또는 객체. 공유 엘리먼트 transition에 사용되는 View Transition의 이름입니다. 제공되지 않으면 React는 예상치 못한 애니메이션을 방지하기 위해 각 View Transition에 대해 고유한 이름을 사용합니다.
콜백
이 콜백을 사용하면 animate API를 사용하여 애니메이션을 명령적으로 조정할 수 있습니다.
- optional
onEnter
: 함수. React는 “enter” 애니메이션 후에onEnter
를 호출합니다. - optional
onExit
: 함수. React는 “exit” 애니메이션 후에onExit
를 호출합니다. - optional
onShare
: 함수. React는 “share” 애니메이션 후에onShare
를 호출합니다. - optional
onUpdate
: 함수. React는 “update” 애니메이션 후에onUpdate
를 호출합니다.
각 콜백은 다음을 인수로 받습니다.
element
: 애니메이션된 DOM 엘리먼트입니다.types
: 애니메이션에 포함된 Transition 타입입니다.
View Transition 클래스
View Transition 클래스는 ViewTransition이 활성화될 때 Transition 중에 React가 적용하는 CSS 클래스 이름입니다. 문자열 또는 객체일 수 있습니다.
string
: 활성화될 때 자식 엘리먼트에 추가되는class
입니다.'none'
이 제공되면 클래스가 추가되지 않습니다.object
: 자식 엘리먼트에 추가되는 클래스는addTransitionType
으로 추가된 View Transition 타입과 일치하는 키입니다. 객체는 일치하는 타입이 없을 때 사용할default
도 지정할 수 있습니다.
값 'none'
은 특정 트리거에 대해 View Transition이 활성화되지 않도록 하는 데 사용할 수 있습니다.
View Transition 스타일링
<ViewTransition>
의 애니메이션을 커스터마이즈하려면 활성화 프로퍼티 중 하나에 View Transition 클래스를 제공할 수 있습니다. View Transition 클래스는 ViewTransition이 활성화될 때 React가 자식 엘리먼트에 적용하는 CSS 클래스 이름입니다.
예를 들어 “enter” 애니메이션을 커스터마이즈하려면 enter
프로퍼티에 클래스 이름을 제공합니다.
<ViewTransition enter="slide-in">
<ViewTransition>
이 “enter” 애니메이션을 활성화하면 React는 클래스 이름 slide-in
을 추가합니다. 그런 다음 View Transition 가상 선택자를 사용하여 이 클래스를 참조하여 재사용 가능한 애니메이션을 구축할 수 있습니다.
::view-transition-group(.slide-in) {
}
::view-transition-old(.slide-in) {
}
::view-transition-new(.slide-in) {
}
향후 CSS 라이브러리에서 View Transition 클래스를 사용한 내장 애니메이션을 추가하여 사용하기 쉽게 만들 수 있습니다.
주의 사항
- 기본적으로
setState
업데이트는 즉시 이루어지며<ViewTransition>
을 활성화하지 않습니다. Transition으로 감싼 업데이트만 해당됩니다.<Suspense>
를 사용하여 Transition을 선택하여 콘텐츠를 표시할 수도 있습니다. <ViewTransition>
은 이동, 확대/축소, 크로스 페이드할 수 있는 이미지를 생성합니다. React Native나 Motion에서 본 레이아웃 애니메이션과 달리 내부의 모든 개별 엘리먼트가 위치에 애니메이션되지는 않습니다. 이는 더 나은 성능과 개별 부분을 애니메이션하는 것과 비교하여 더 연속적이고 부드러운 애니메이션 느낌을 줄 수 있습니다. 그러나 스스로 움직여야 하는 것들의 연속성을 잃을 수도 있습니다. 결과적으로 더 많은<ViewTransition>
경계를 수동으로 추가해야 할 수 있습니다.- 많은 사용자가 페이지에서 애니메이션을 선호하지 않을 수 있습니다. React는 이 경우 애니메이션을 자동으로 비활성화하지 않습니다. 사용자 선호도에 따라
@media (prefers-reduced-motion)
미디어 쿼리를 사용하여 애니메이션을 비활성화하거나 약하게 만드는 것을 권장합니다. 향후 CSS 라이브러리에서는 프리셋에 이것이 내장되어 있을 수 있습니다. - 현재
<ViewTransition>
은 DOM에서만 작동합니다. React Native 및 다른 플랫폼에 대한 지원을 추가하는 작업을 진행하고 있습니다.
사용법
enter/exit에서 엘리먼트 애니메이션 적용하기
Enter/Exit Transition은 <ViewTransition>
이 Transition에서 컴포넌트에 의해 추가되거나 제거될 때 발생합니다.
function Child() {
return <ViewTransition>Hi</ViewTransition>
}
function Parent() {
const [show, setShow] = useState();
if (show) {
return <Child />;
}
return null;
}
setShow
가 호출되면 show
가 true
로 바뀌고 Child
컴포넌트가 렌더링됩니다. setShow
가 startTransition
내부에서 호출되고 Child
가 다른 DOM 노드보다 먼저 ViewTransition
을 렌더링하면 enter
애니메이션이 발생합니다.
show
가 다시 false
로 바뀌면 exit
애니메이션이 발생합니다.
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from 'react'; import {Video} from "./Video"; import videos from "./data" function Item() { return ( <ViewTransition> <Video video={videos[0]}/> </ViewTransition> ); } export default function Component() { const [showItem, setShowItem] = useState(false); return ( <> <button onClick={() => { startTransition(() => { setShowItem((prev) => !prev); }); }} >{showItem ? '➖' : '➕'}</button> {showItem ? <Item /> : null} </> ); }
공유 엘리먼트 애니메이션 적용하기
일반적으로 <ViewTransition>
에 이름을 할당하는 것보다 React가 자동으로 이름을 할당하도록 하는 것을 권장합니다. 이름을 할당하고 싶은 경우는 하나의 트리가 마운트 해제되고 다른 트리가 동시에 마운트될 때 완전히 다른 컴포넌트 간에 애니메이션을 적용하여 연속성을 보존하고자 할 때입니다.
<ViewTransition name={UNIQUE_NAME}>
<Child />
</ViewTransition>
하나의 트리가 마운트 해제되고 다른 트리가 마운트될 때 마운트 해제되는 트리와 마운트되는 트리에서 동일한 이름이 존재하는 쌍이 있으면 둘 다에서 “share” 애니메이션이 발생합니다. 마운트 해제되는 쪽에서 마운트되는 쪽으로 애니메이션이 적용됩니다.
exit/enter 애니메이션과 달리 삭제되거나 새로 마운트된 트리의 깊숙한 곳에서도 적용될 수 있습니다. <ViewTransition>
이 exit/enter에도 해당한다면 “share” 애니메이션이 우선순위를 갖습니다.
Transition이 먼저 한쪽을 마운트 해제하고 새로운 이름이 마운트되기 전에 <Suspense>
폴백이 표시되는 경우 공유 엘리먼트 Transition은 발생하지 않습니다.
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from "react"; import {Video, Thumbnail, FullscreenVideo} from "./Video"; import videos from "./data"; export default function Component() { const [fullscreen, setFullscreen] = useState(false); if (fullscreen) { return <FullscreenVideo video={videos[0]} onExit={() => startTransition(() => setFullscreen(false))} /> } return <Video video={videos[0]} onClick={() => startTransition(() => setFullscreen(true))} /> }
목록에서 항목 순서 변경 애니메이션 적용하기
items.map(item => <Component key={item.id} item={item} />)
콘텐츠를 업데이트하지 않고 목록 순서를 변경할 때 DOM 노드 밖에 있으면 목록의 각 <ViewTransition>
에서 “update” 애니메이션이 발생합니다. enter/exit 애니메이션과 유사합니다.
이는 이 <ViewTransition>
에서 애니메이션이 발생한다는 의미입니다.
function Component() {
return <ViewTransition><div>...</div></ViewTransition>;
}
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from "react"; import {Video} from "./Video"; import videos from "./data"; export default function Component() { const [orderedVideos, setOrderedVideos] = useState(videos); const reorder = () => { startTransition(() => { setOrderedVideos((prev) => { return [...prev.sort(() => Math.random() - 0.5)]; }); }); }; return ( <> <button onClick={reorder}>🎲</button> <div className="listContainer"> {orderedVideos.map((video, i) => { return ( <ViewTransition key={video.title}> <Video video={video} /> </ViewTransition> ); })} </div> </> ); }
하지만 다음은 각 개별 항목에 애니메이션을 적용하지 않습니다.
function Component() {
return <div><ViewTransition>...</ViewTransition></div>;
}
대신 부모 <ViewTransition>
이 크로스 페이드됩니다. 부모 <ViewTransition>
이 없으면 별도의 애니메이션이 적용되지 않습니다.
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from "react"; import {Video} from "./Video"; import videos from "./data"; export default function Component() { const [orderedVideos, setOrderedVideos] = useState(videos); const reorder = () => { startTransition(() => { setOrderedVideos((prev) => { return [...prev.sort(() => Math.random() - 0.5)]; }); }); }; return ( <> <button onClick={reorder}>🎲</button> <ViewTransition> <div className="listContainer"> {orderedVideos.map((video, i) => { return <Video video={video} key={video.title} />; })} </div> </ViewTransition> </> ); }
이는 컴포넌트가 자체적으로 순서 변경 애니메이션을 제어할 수 있도록 하고 싶을 때는 리스트 안에 래퍼 요소를 두지 않는 것이 좋다는 뜻입니다.
items.map(item => <div><Component key={item.id} item={item} /></div>)
위 규칙은 항목 중 하나가 크기 조정을 위해 업데이트되어 형제 항목들이 크기 조정되는 경우에도 적용되며, 이는 형제 <ViewTransition>
도 애니메이션시키지만 직접적인 형제인 경우에만 해당합니다.
이것은 업데이트가 발생하여 레이아웃이 크게 변경될 때, 페이지에 있는 모든 <ViewTransition>
을 각각 개별적으로 애니메이션하지 않는다는 뜻입니다. 그렇게 하면 실제 변화와 관계없는 많은 산만한 애니메이션이 발생해 주의를 흐트러뜨리게 됩니다. 따라서 React는 개별 애니메이션을 언제 트리거할지에 대해 보다 보수적으로 동작합니다.
Suspense 콘텐츠에서 애니메이션 적용하기
다른 Transition과 마찬가지로 React는 애니메이션을 실행하기 전에 데이터와 새로운 CSS(<link rel="stylesheet" precedence="...">
)를 기다립니다. 이에 더해 ViewTransition은 새로운 폰트가 나중에 깜빡이는 것을 방지하기 위해 애니메이션을 시작하기 전에 새로운 폰트가 로드될 때까지 최대 500ms까지 기다립니다. 같은 이유로 ViewTransition으로 래핑된 이미지는 이미지가 로드될 때까지 기다립니다.
새로운 Suspense 경계 인스턴스 내부에 있으면 폴백이 먼저 표시됩니다. Suspense 경계가 완전히 로드된 후 <ViewTransition>
이 콘텐츠로 전환되는 애니메이션을 실행합니다.
현재 이 동작은 클라이언트 측 Transition에서만 발생합니다. 향후에는 초기 로드 중에 서버의 콘텐츠가 일시 중단될 때 스트리밍 SSR에 대한 Suspense 경계도 애니메이션할 예정입니다.
<ViewTransition>
을 배치하는 위치에 따라 Suspense 경계를 애니메이션하는 두 가지 방법이 있습니다.
Update:
<ViewTransition>
<Suspense fallback={<A />}>
<B />
</Suspense>
</ViewTransition>
이 시나리오에서 콘텐츠가 A에서 B로 바뀔 때 “update”로 처리되며 적절한 경우 해당 클래스를 적용합니다. A와 B 모두 동일한 view-transition-name을 갖게 되므로 기본적으로 크로스 페이드로 작동합니다.
import { unstable_ViewTransition as ViewTransition, useState, startTransition, Suspense } from 'react'; import {Video, VideoPlaceholder} from "./Video"; import {useLazyVideoData} from "./data" function LazyVideo() { const video = useLazyVideoData(); return ( <Video video={video}/> ); } export default function Component() { const [showItem, setShowItem] = useState(false); return ( <> <button onClick={() => { startTransition(() => { setShowItem((prev) => !prev); }); }} >{showItem ? '➖' : '➕'}</button> {showItem ? ( <ViewTransition> <Suspense fallback={<VideoPlaceholder />}> <LazyVideo /> </Suspense> </ViewTransition> ) : null} </> ); }
Enter/Exit:
<Suspense fallback={<ViewTransition><A /></ViewTransition>}>
<ViewTransition><B /></ViewTransition>
</Suspense>
이 시나리오에서는 각각 고유한 view-transition-name
을 갖는 두 개의 별도 ViewTransition 인스턴스입니다. 이는 <A>
의 “exit”와 <B>
의 “enter”로 처리됩니다.
<ViewTransition>
경계를 배치하는 위치에 따라 다른 효과를 얻을 수 있습니다.
애니메이션 제외하기
때로는 전체 페이지와 같은 큰 기존 컴포넌트를 래핑하고 테마 변경과 같은 일부 업데이트를 애니메이션하고 싶지만 전체 페이지 내부의 모든 업데이트가 업데이트될 때 크로스 페이드에 포함되는 것을 원하지 않을 수 있습니다. 특히 점진적으로 더 많은 애니메이션을 추가하는 경우에 그렇습니다.
클래스 “none”을 사용하여 애니메이션을 제외할 수 있습니다. 자식을 “none”으로 래핑하면 부모가 여전히 발생하는 동안 자식에 대한 업데이트 애니메이션을 비활성화할 수 있습니다.
<ViewTransition>
<div className={theme}>
<ViewTransition update="none">
{children}
</ViewTransition>
</div>
</ViewTransition>
이는 테마가 변경될 때만 애니메이션되며 자식만 업데이트될 때는 애니메이션되지 않습니다. 자식은 여전히 자체 <ViewTransition>
으로 다시 참여할 수 있지만 최소한 다시 수동으로 제어하는 방식이 됩니다.
애니메이션 커스터마이징
기본적으로 <ViewTransition>
은 브라우저의 기본 크로스 페이드를 포함합니다.
애니메이션을 커스터마이징하려면 <ViewTransition>
컴포넌트에 props를 제공하여 <ViewTransition>
이 활성화되는 방식에 따라 사용할 애니메이션을 지정할 수 있습니다.
예를 들어 기본 크로스 페이드 애니메이션을 느리게 할 수 있습니다.
<ViewTransition default="slow-fade">
<Video />
</ViewTransition>
그리고 View Transition 클래스를 사용하여 CSS에서 slow-fade를 정의합니다.
::view-transition-old(.slow-fade) {
animation-duration: 500ms;
}
::view-transition-new(.slow-fade) {
animation-duration: 500ms;
}
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from 'react'; import {Video} from "./Video"; import videos from "./data" function Item() { return ( <ViewTransition default="slow-fade"> <Video video={videos[0]}/> </ViewTransition> ); } export default function Component() { const [showItem, setShowItem] = useState(false); return ( <> <button onClick={() => { startTransition(() => { setShowItem((prev) => !prev); }); }} >{showItem ? '➖' : '➕'}</button> {showItem ? <Item /> : null} </> ); }
default
설정 외에도 enter
, exit
, update
, share
애니메이션에 대한 구성을 제공할 수 있습니다.
import { unstable_ViewTransition as ViewTransition, useState, startTransition } from 'react'; import {Video} from "./Video"; import videos from "./data" function Item() { return ( <ViewTransition enter="slide-in" exit="slide-out"> <Video video={videos[0]}/> </ViewTransition> ); } export default function Component() { const [showItem, setShowItem] = useState(false); return ( <> <button onClick={() => { startTransition(() => { setShowItem((prev) => !prev); }); }} >{showItem ? '➖' : '➕'}</button> {showItem ? <Item /> : null} </> ); }
타입으로 애니메이션 커스터마이징하기
특정 활성화 트리거에 대해 특정 Transition 타입이 활성화될 때 자식 엘리먼트에 클래스 이름을 추가하기 위해 addTransitionType
API를 사용할 수 있습니다. 이를 통해 각 Transition 타입에 대한 애니메이션을 커스터마이징할 수 있습니다.
예를 들어 모든 앞으로 및 뒤로 네비게이션에 대한 애니메이션을 커스터마이징하려면,
<ViewTransition default={{
'navigation-back': 'slide-right',
'navigation-forward': 'slide-left',
}}>
<div>...</div>
</ViewTransition>
// 라우터에서:
startTransition(() => {
addTransitionType('navigation-' + navigationType);
});
ViewTransition이 “navigation-back” 애니메이션을 활성화하면 React는 “slide-right” 클래스 이름을 추가합니다. ViewTransition이 “navigation-forward” 애니메이션을 활성화하면 React는 “slide-left” 클래스 이름을 추가합니다.
향후 라우터와 다른 라이브러리들이 표준 view-transition 타입과 스타일에 대한 지원을 추가할 수 있습니다.
import { unstable_ViewTransition as ViewTransition, unstable_addTransitionType as addTransitionType, useState, startTransition, } from "react"; import {Video} from "./Video"; import videos from "./data" function Item() { return ( <ViewTransition enter={ { "add-video-back": "slide-in-back", "add-video-forward": "slide-in-forward" } } exit={ { "remove-video-back": "slide-in-forward", "remove-video-forward": "slide-in-back" } }> <Video video={videos[0]}/> </ViewTransition> ); } export default function Component() { const [showItem, setShowItem] = useState(false); return ( <> <div className="button-container"> <button onClick={() => { startTransition(() => { if (showItem) { addTransitionType("remove-video-back") } else { addTransitionType("add-video-back") } setShowItem((prev) => !prev); }); }} >⬅️</button> <button onClick={() => { startTransition(() => { if (showItem) { addTransitionType("remove-video-forward") } else { addTransitionType("add-video-forward") } setShowItem((prev) => !prev); }); }} >➡️</button> </div> {showItem ? <Item /> : null} </> ); }
View Transition 지원 라우터 구축하기
스크롤 복원이 애니메이션 중에 정상적으로 동작하도록, React는 대기 중인 내비게이션이 완료될 때까지 기다립니다. 네비게이션이 React에서 차단되는 경우 useEffect
는 교착 상태로 이어질 수 있으므로 라우터는 useLayoutEffect
에서 차단을 해제해야 합니다.
”뒤로” 네비게이션 중처럼 레거시 popstate 이벤트에서 startTransition
이 시작되면 스크롤과 폼 복원이 올바르게 작동하도록 동기적으로 완료되어야 합니다. 이는 View Transition 애니메이션 실행과 충돌합니다. 따라서 React는 popstate에서 애니메이션을 건너뜁니다. 따라서 뒤로 버튼에 대해서는 애니메이션이 실행되지 않습니다. Navigation API를 사용하도록 라우터를 업그레이드하여 이를 해결할 수 있습니다.
문제 해결
<ViewTransition>
이 활성화되지 않습니다
<ViewTransition>
은 DOM 노드보다 앞에 배치되어야만 활성화됩니다.
function Component() {
return (
<div>
<ViewTransition>Hi</ViewTransition>
</div>
);
}
해결하려면 <ViewTransition>
이 다른 DOM 노드보다 앞에 오도록 하세요.
function Component() {
return (
<ViewTransition>
<div>Hi</div>
</ViewTransition>
);
}
”동일한 이름으로 마운트된 <ViewTransition name=%s>
컴포넌트가 두 개 있습니다.”라는 오류가 발생합니다
이 오류는 동일한 name
을 가진 두 개의 <ViewTransition>
컴포넌트가 동시에 마운트될 때 발생합니다.
function Item() {
// 🚩 모든 항목이 동일한 "name"을 갖게 됩니다.
return <ViewTransition name="item">...</ViewTransition>;
}
function ItemList({items}) {
return (
<>
{item.map(item => <Item key={item.id} />)}
</>
);
}
이는 View Transition에서 오류를 발생시킵니다. 개발 중에 React는 이 문제를 감지하여 표면화하고 두 개의 오류를 기록합니다.
<ViewTransition name=%s>
components with the same name mounted at the same time. This is not supported and will cause View Transitions to error. Try to use a more unique name e.g. by using a namespace prefix and adding the id of an item to the name.
at Item
at ItemList<ViewTransition name=%s>
duplicate has this stack trace.
at Item
at ItemList해결하려면 name
이 고유하도록 하거나 이름에 id
를 추가하여 전체 앱에서 동일한 이름을 가진 <ViewTransition>
이 한 번에 하나만 마운트되도록 하세요.
function Item({id}) {
// ✅ 모든 항목이 고유한 "name"을 갖게 됩니다.
return <ViewTransition name={`item-${id}`}>...</ViewTransition>;
}
function ItemList({items}) {
return (
<>
{item.map(item => <Item key={item.id} item={item} />)}
</>
);
}