react-transition-group은 react 에서 좀 더 쉽게 애니메이션 효과를 구현할 수 있는 라이브러리로, react의 라이프 사이클 메서드와 함께 작동하여 react 구성 요소 간의 변화를 감지하고, 변화에 대한 애니메이션을 적용할수 있다.
$ yarn add react-transition-group
$ yarn add -D @types/react-transition-group
react 컴포넌트가 mount , unmount , update 될 때 애니메이션을 적용할 수 있는 컴포넌트이다. react 라이프 사이클과 함께 작동하여 변화에 대한 애니메이션을 적용할 수 있다.
CSS animation 을 적용할 수 있는 컴포넌트이다. CSS class 를 추가, 제거 또는 변경하여 애니메이션을 적용할 수 있다.
다른 컴포넌트로 전환될 때, 애니메이션을 적용할 수 있는 컴포넌트이다. key prop 을 이용하여 현재 보이는 컴포넌트와 새로운 컴포넌트를 전환할 때 애니메이션을 적용할 수 있다.
리스트를 관리할 때 사용되며, 리스트에 새로운 아이템이 추가, 제거 또는 변경될 때 애니메이션을 적용할 수 있다. 리스트 아이템을 key prop 으로 구분하고, 이를 기반으로 애니메이션을 적용한다.
import { Transition } from 'react-transition-group';
export default function Index() {
const [isView, setIsView] = useState(false);
const defaultStyle = {
transition: `opacity 300ms ease-in-out`,
};
const transitionStyles: Record<TransitionStatus, CSSProperties> = {
entering: { opacity: 1 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
exited: { opacity: 0 },
unmounted: { opacity: 0 },
};
return (
<S.Wrap>
<S.Button onClick={() => setIsView((prev) => !prev)}>toggle</S.Button>
<Transition in={isView} timeout={300} mountOnEnter unmountOnExit>
{(state) => (
// state: ** -> entering -> entered -> exiting -> exited -> **
<S.Box style={{ ...defaultStyle, ...transitionStyles[state] }}>Animation Box</S.Box>
)}
</Transition>
</S.Wrap>
);
}
entering , entered , exiting , exited, unmounted 가 존재한다.in 값에 따라 state 상태값이 변경된다.true : entering -> enteredfalse : exiting -> exited"timeout" : 전환 효과가 재생되는 시간
"mountOnEnter" : 해당 옵션이 존재시 in 값이 true 가 되야만 렌더링 (이 옵션이 없다면 초기값이 false 일때도 dom tree에 존재하나 보이지만 않음)
"unmountOnExit" : 해당 옵션이 존재시 in 값이 false 가 될때, dom tree에서도 제거 (이 옵션이 없다면 true -> false 로 변경시 보이지만 않을 뿐 여전히 dom tree에 존재)
"on${status}" : 해당 상태로 변경된 직후 실행되는 함수
import { CSSTransition } from 'react-transition-group';
import './Transition.css';
export default function Index() {
const [isView, setIsView] = useState(false);
return (
<S.Wrap>
<S.Button onClick={() => setIsView((prev) => !prev)}>toggle</S.Button>
<CSSTransition in={isView} classNames="opacity" timeout={300} mountOnEnter unmountOnExit>
<S.Box>Animation Box</S.Box>
</CSSTransition>
</S.Wrap>
);
}
위의 Transition 컴포넌트 사용법과의 가장 큰 차이점은 classNames 를 props로 받는다는 것이다.
in , classNames 의 값에 따라 다음과 같은 순으로 class 명이 변경된다
${classNames}-enter -> ${classNames}-enter-active -> ${classNames}-enter-done -> ${classNames}-exit -> ${classNames}-exit-active -> ${classNames}-exit-done
이러한 class에 맞춰서 아래와 같이 css 를 작성하여 import해 사용하였다.
.opacity-enter {
opacity: 0;
}
.opacity-enter-active {
opacity: 1;
transition: opacity 0.3s;
}
.opacity-exit {
opacity: 1;
}
.opacity-exit-active {
opacity: 0;
transition: opacity 0.3s;
}
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import './Transition.css';
export default function Index() {
const [boxes, setBoxes] = useState<number[]>([]);
const addBox = () => setBoxes((prev) => [...prev, Math.random()]);
const deleteBox = (i: number) => setBoxes((prev) => prev.filter((_, idx) => idx !== i));
return (
<S.Wrap>
<S.Button onClick={addBox}>add</S.Button>
<TransitionGroup className="boxes">
{boxes.map((box, idx) => (
<CSSTransition key={idx} timeout={300} classNames="list">
<S.Box onClick={() => deleteBox(idx)}>{box}</S.Box>
</CSSTransition>
))}
</TransitionGroup>
</S.Wrap>
);
}
리스트를 그룹화하여 요소가 추가, 삭제 될때 리스트 요소에 대해 애니메이션을 적용한다.
따로 in 을 작성하지 않아도 된다.
.list-enter {
transform: translateY(-20%);
opacity: 0;
}
.list-enter-active {
transform: translateY(0);
opacity: 1;
transition: all 0.3s;
}
.list-exit {
transform: translateY(0);
opacity: 1;
}
.list-exit-active {
transform: translateY(-20%);
opacity: 0;
transition: all 0.3s;
}
여긴 잘 안써서, 나중에 쓸일 있으면 작성...