Collapse 컴포넌트의 동작을 구현하기 위해 CSSTransition 컴포넌트를 활용하자.
먼저 타입 인터페이스를 작성한다. 이때 duration 속성은 트랜지션 효과의 지속 시간을 지정하는 속성이다.
스타일시트에 작성하지 않고 프로퍼티를 통해 전달받도록 지정한 부분을 기억해두자.
Collapse.client.tsx
1import{ type PropsWithChildren }from"react";23exportinterfaceCollapsePropsextendsPropsWithChildren{4 className?: string;// Collapse 컨테이너에 스타일 오버라이드가 필요한 경우5 isOpen?: boolean;// 부모 컴포넌트에서 제어하는 Collapse 여닫음 상태값6 appear?: boolean;// Collpase가 처음에 열린 상태로 표시될 때 열리는 트랜지션 효과를 부여할지 여부7 duration?: number;// 트랜지션 효과의 지속 시간8 onEnter?:()=>void;// Collapse가 열리기 시작할 때 호출되는 콜백 함수9 onEntered?:()=>void;// Collpse가 완전히 열린 후 호출되는 콜백 함수10 onExit?:()=>void;// Collapse가 닫히기 시작할 때 호출되는 콜백 함수11 onExited?:()=>void;// Collapse가 완전히 닫힌 후 호출되는 콜백 함수12}
여기서 알아가야할 중요한 점은 트랜지션 효과가 부여되기 위해서는 수치값에 기반한 스타일 속성값이 필요하다는 부분이다.
예를 들어, 열리는 단계 onEnter, onEntering, onEntered를 살펴보자.
열리기 시작하는 시점에서는 height: 0px로 설정한다.
열리는 과정의 시점에서는 height: auto가 아닌 콘텐츠 영역의 높이를 getHeight 함수를 통해 수치값으로 설정한다.
열리고 난 이후의 시점에서는 다시 height: auto로 설정한다. 이 이유는 Collapse가 열리고 난 이후 높이값이 특정 px로 고정되어 있는 상태로 머물러 있게 되면 콘텐츠의 내용이 변화하여 콘텐츠 영역의 높이가 변경되더라도 Collapse 컨테이너의 높이는 콘텐츠의 높이를 따라가지 못하는 상태가 되기 때문이다.
Collapse.client.tsx
1exportdefaultfunctionCollapse({...}: CollapseProps){2const nodeRef = useRef<HTMLDivElement>(null);3const[height, setHeight]=useState(isOpen ?'auto':'0px');45constgetHeight=()=>{6return nodeRef.current ?`${nodeRef.current.scrollHeight}px`:'0px';7};89constonEnter=()=>{10 onEnterProp?.();11setHeight('0px');12};1314constonEntering=()=>{15setHeight(getHeight());// 트랜지션 효과가 발생하기 위해 수치값으로 설정합니다.16};1718constonEntered=()=>{19 onEnteredProp?.();20setHeight('auto');// 열리고 난 이후에는 콘텐츠 영역의 높이를 따라가도록 합니다.21};2223constonExit=()=>{24 onExitProp?.();25setHeight(getHeight());26};2728constonExiting=()=>{29setHeight('0px');30};3132constonExited=()=>{33 onExitedProp?.();34};3536return(37<CSSTransition
38 nodeRef={nodeRef}39 timeout={duration}40in={isOpen}41 appear={appear}42 onEnter={onEnter}43 onEntering={onEntering}44 onEntered={onEntered}45 onExit={onExit}46 onExiting={onExiting}47 onExited={onExited}48>49<div
50 style={{ height,transitionDuration:`${duration}ms`}}51 className={clsx('broccoli-ui-collapse', className)}52 ref={nodeRef}53>54{children}55</div>56</CSSTransition>57);58}