[React] React Transition Group 사용하기
아래 화면은 필터 종류에 따라 filteredProjects 라는 상태에 프로젝트 배열을 저장하여 보여주고 있다.
하지만 React에서는 상태 변화나 DOM 요소의 변화가 발생할 때, 애니메이션이나 transition 효과가 적용되지 않는다.
따라서 기존처럼 transition만 적용한다면, 아래와 같이 opacitiy transition 효과가 적용되지 않을 것이다.
이를 위해 컴포넌트가 DOM에 마운팅되고 언마운팅되는 상황에 애니메이션을 적용할 수 있도록 도와주는 React에서 자체적으로 제공하는 라이브러리가 있는데, 그게 바로 react-transition-group 이다.
React Transition Group은 다음과 같은 컴포넌트 생애주기를 직접 제어할 수 있는 class를 제공한다.
- fade-enter: 컴포넌트가 처음 생성될 때 적용
- fade-enter-active: 진입 애니메이션이 활성화되는 동안 적용
- fade-exit: 컴포넌트가 제거될 때 적용
- fade-exit-active: 제거 애니메이션이 활성화되는 동안 적용
TransitionGroup과 CSSTransition의 역할
- TransitionGroup
- 필터링된 프로젝트 리스트를 렌더링할 때, 각 프로젝트 카드의 진입(enter)과 퇴장(exit)을 감싸주는 역할
- 변경되는 리스트의 개별 항목을 추적하여 애니메이션 효과를 적용
- CSSTransition
- 개별 프로젝트 카드에 진입/퇴장 애니메이션을 지정할 수 있다.
- classNames를 사용해 애니메이션에 필요한 CSS 클래스 이름을 부여할 수 있다.
1. 애니메이션 효과 정의
CSSTransition의 classNames와 CSS 클래스의 네이밍 규칙을 통해 애니메이션을 정의한다.
const ProjectLinkWrapper = styled.a<{ isFirst: boolean }>`
transition: opacity 300ms ease-in-out;
&.fade-enter {
opacity: 0;
}
&.fade-enter-active {
opacity: 1;
}
&.fade-exit {
opacity: 1;
}
&.fade-exit-active {
opacity: 0;
}
`;
2. 구현
프로젝트 리스트 컴포넌트 내부에서 TransitionGroup으로 프로젝트 리스트를 감싸고, 각 프로젝트 카드에 CSSTransition을 추가한다.
<TransitionGroup>의 component={null} ?
TransitionGroup은 기본적으로 div 같은 HTML 태그로 렌더링된다.
나는 프로젝트 리스트 컴포넌트의 자식 요소에 grid 스타일 요소를 사용하고 있어서, TransitionGroup으로 인해 div 컴포넌트가 중간에 렌더링되면 grid 스타일이 깨질 수 있다.
하지만, component={null} 속성을 사용하면 TransitionGroup이 렌더링할 실제 DOM 요소를 생성하지 않고, 자식 요소만 렌더링하도록 할 수 있다
<TransitionGroup component={null}>
{filteredProjects.map((project, index) => (
<CSSTransition
key={`${project.id}-${filter}`}
timeout={300} // 애니메이션 지속 시간
classNames="fade" // 애니메이션 클래스 이름 지정
>
<ProjectLinkWrapper href="#" isFirst={index === 0 && filter === "All"}>
<ProjectCard>
{/* 카드 내용 */}
</ProjectCard>
</ProjectLinkWrapper>
</CSSTransition>
))}
</TransitionGroup>