리액트에서 CSS transition을 이용해서 탭 컴포넌트가 브라우저 상에서 서서히 나타나도록 구현해보자.
CSS 트랜지션은 어떤 엘리먼트에 스타일의 변화가 일정 시간에 걸쳐서 부드럽게 나타나도록 하는 속성이다.
사이트에서 애니메이션을 구현할때 유용하다.
애니메이션 동작 전 스타일을 담을 className 만들기
애니메이션 동작 후 스타일을 담을 className 만들기
transition 속성도 추가
원할 때 2번 탈부착
.start{
opacity: 0;
}
.end{
opacity: 1;
transition: opacity 0.5s;
}
시작하면 투명도가 0 이었다가 0.5s의 속도로 애니메이션이 끝날때 투명도가 1로 변경된다.
function TabContent ({tab}){
return (<div className="start end">
{[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
</div>)
}
저 end 클래스를 지웠다가 다시 부착해보면 탭 컴포넌트가 서서히 나타나는 것을 확인해볼 수 있다.
useEffect(() => {
},[tab])
이런식으로 useEffect() 를 작성하면 tab이라는 state 가 변경될때마다 useEffect() 안에 있는 내용이 실행 되는 것이다.
function TabContent ({tab}){
let [fade, setFade]= useState('');
useEffect(() => {
setFade('end');
},[tab])
return (<div className={"start " + fade}>
{[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
</div>)
}
fade와 setFade state를 만들어주고, tab state가 변경될때 div의 className에 end가 추가 될 수 있도록 하였다.
❗️ 하지만 이렇게 하면 className 에 end가 추가되긴 하지만 애니메이션이 적용되지는 않는다. 왜일까? ❗️ 애니메이션이 적용되려면 애니메이션이 동작 후에 적용될 스타일이 떼어졌다가 붙여져야 작동한다. 지금의 저 상태는 end 가 추가만 되고 다시 삭제가 되지는 않는 상태이다.
useEffect(() => {
setFade('end');
return () => {
setFade('')
}
},[tab])
useEffect()가 실행되기 전에 먼저 실행되는 clean up function 을 이용해서 먼저 setFade를 ' ' 로 만들어주고
그 후에 다시 end를 붙여주도록 하였다.
❗️ 하지만 이렇게 해도 또 작동하지 않는다 ❗️ 왜일까?
리액트 18버전 이상에서 automatic batching 이라는 기능이 있는데, 이 자동 배칭기능이란 리액트의 더 나은 성능 향상을 위해 여러개의 state 업데이트가 있더라도 한번만 리렌더링 해주는 것이다.
원래라면 state 변경 함수를 작동 시킬때마다 리렌더링을 해주는데, 자동 배칭을 통해서 state 변경함수가 여러개 있더라도 한번만 리렌더링을 해주는 것이다.
그렇기 때문에 위의 코드도 원래라면 fade state 를 ' ' 로 초기화 시켜주고, 다시 end 로 변경해주어야 하는데, 한번만 리렌더링 되기 때문에 end만 추가 되는 것이다.
useEffect(() => {
setTimeout(() => {
setFade('end')
}, 100);
return () => {
setFade('')
}
},[tab])
flushSync() 나 setTimeout() 을 이용하면 auto batching 을 막아줄수있다.
저런식으로 clean up function 과 state 변경 함수 사이에 텀을 주면 정상적으로 애니메이션이 작동한다.