개요
<Activity>
는 UI의 일부를 숨기거나 보여줄 수 있게 해주는 React의 실험적(Experimental) 컴포넌트입니다. "숨김(hidden)" 상태에서는 해당 UI가 화면에 보이지 않으며, 이때 React는 이 부분의 Effect를 언마운트하지만, React/DOM state는 보존합니다. "보임(visible)" 상태로 전환하면, 보존된 state를 그대로 사용해 빠르게 다시 보여줄 수 있습니다.
⚠️ 이 API는 실험적이며, 정식(stable) 릴리즈에는 포함되어 있지 않습니다. 프로덕션에서는 사용하지 마세요.
import { unstable_Activity as Activity } from 'react';
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
Reference
<Activity>
UI의 일부를 <Activity>
로 감싸면, 해당 부분의 가시성(visible/hidden) 상태를 관리할 수 있습니다.
Props
children
: 렌더링할 실제 UI.mode
(optional): "visible" 또는 "hidden". 기본값은 "visible"입니다. "hidden"일 때는 children이 화면에 보이지 않고, Effect가 생성되지 않습니다. "visible"에서 "hidden"으로 전환 시 Effect가 파괴(cleanup)되지만, state는 보존됩니다.
Caveats(주의사항)
- "hidden" 상태에서는 children이 화면에 보이지 않습니다.
- "visible"에서 "hidden"으로 전환 시, 모든 Effect가 언마운트(cleanup)되지만 React/DOM state는 파괴되지 않습니다. 즉, mount 시 1회만 실행되는 Effect는 다시 "visible"로 전환할 때 재실행됩니다.
와 함께 사용하면, 예상치 못한 부작용을 미리 발견할 수 있습니다. 과 함께 사용 시, hidden에서 visible로 전환하면 "enter" 애니메이션, visible에서 hidden으로 전환하면 "exit" 애니메이션이 동작합니다. <Activity mode="hidden">
로 감싼 부분은 SSR(서버사이드 렌더링) 결과에 포함되지 않습니다.<Activity mode="visible">
로 감싼 부분은 다른 콘텐츠보다 낮은 우선순위로 하이드레이션됩니다.
사용법
UI 일부를 미리 프리렌더링하기
<Activity mode={tab === "posts" ? "visible" : "hidden"}>
<PostsTab />
</Activity>
- "hidden" 상태로 렌더링하면, 해당 children은 화면에 보이지 않지만 낮은 우선순위로 프리렌더링됩니다. 이후 "visible"로 전환하면, 이미 준비된 children이 빠르게 마운트됩니다. 탭 UI 등에서 다음에 보여줄 콘텐츠를 미리 준비할 때 유용합니다.
UI 일부의 state를 보존하기
<Activity mode={tab === "posts" ? "visible" : "hidden"}>
<PostsTab />
</Activity>
- "visible"에서 "hidden"으로 전환하면, children의 Effect는 언마운트되지만 React/DOM state는 보존됩니다. 다시 "visible"로 전환하면, 이전 state가 그대로 복원됩니다. 예를 들어, 입력 폼의 draft 내용, 스크롤 위치 등을 유지할 수 있습니다.
트러블슈팅
숨겨진 Activity에서 Effect가 동작하지 않음
<Activity>
가 "hidden" 상태일 때는 모든 Effect가 언마운트됩니다. 이는 구독(subscription) 등 불필요한 작업을 줄이고, 예를 들어 비디오 재생을 일시정지(pause)하는 등의 cleanup이 정상적으로 동작하게 합니다. 다시 "visible"로 전환하면 Effect가 재실행됩니다.
예시: 비디오 플레이어
import { useState, useRef, useEffect, unstable_Activity as Activity } from 'react';
function VideoPlayer({ src }) {
const ref = useRef(null);
useEffect(() => {
const videoRef = ref.current;
videoRef.play();
return () => {
videoRef.pause();
};
}, []);
return <video ref={ref} src={src} muted loop playsInline />;
}
export default function App() {
const [video, setVideo] = useState(1);
return (
<>
<button onClick={() => setVideo(1)}>Big Buck Bunny</button>
<button onClick={() => setVideo(2)}>Elephants Dream</button>
<Activity mode={video === 1 ? 'visible' : 'hidden'}>
<VideoPlayer src="https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4" />
</Activity>
<Activity mode={video === 2 ? 'visible' : 'hidden'}>
<VideoPlayer src="https://archive.org/download/ElephantsDream/ed_1024_512kb.mp4" />
</Activity>
</>
);
}
- 단순히
display: none
으로 숨기면, 비디오가 백그라운드에서 계속 재생될 수 있습니다. Activity를 사용하면, 숨겨진 비디오의 Effect가 언마운트되어 재생이 멈춥니다.
SSR에서 hidden Activity가 렌더링되지 않음
<Activity mode="hidden">
로 감싼 부분은 SSR 결과에 포함되지 않습니다. 서버에서 해당 콘텐츠가 필요하다면, useDeferredValue 등 다른 방법을 사용해야 합니다.
반응형