[framer-motion] AnimatePresence 분석하기

Jiseong·2023년 8월 6일
4

react

목록 보기
5/5
post-thumbnail

Intro

소프티어 팀프로젝트 진행 중에 페이지 전환 애니메이션구현에서 막혔다.
다음 페이지가 나타나는 애니메이션 적용은 쉽다.
라우팅을하며 사라질 컴포넌트에 대해 애니메이션을 적용해야한다.

다른 팀들은 라우팅을 포기하고 모든 페이지를 렌더링하여 Carousel 방식으로 구현했다.
하지만 나는 필요 없는 페이지까지 모두 렌더링을 한다는 점이 마음에 들지 않았다.

framer-motion 라이브러리를 쓰며 비슷한 애니메이션을 구현한 것이 생각났다.

분석하기

핵심 원리

  1. DOM에서 언마운트 될 컴포넌트를 찾는다.
  2. 언마운트 될 컴포넌트를 복제하여 렌더링한다.
  3. 복제한 컴포넌트에 애니메이션을 적용한다.
  4. 복제한 컴포넌트를 DOM에서 제거한다.

이것만 하면 끝이다.

-분석 끝-

구현

코드 위주의 설명은 너무 복잡해서 그림으로 설명한다.

라우터 커스텀

export default function App() {
  return (
    <BrowserRouter>
      <NavBar />
      <CustomRoutes />
    </BrowserRouter>
  );
}
export default function CustomRoutes() {
  const location = useLocation();

  return (
    <AnimatePresence>
      {location.pathname === PATH.trim && (
        <PageAnimationWrapper key={'trim'}>
          <TrimPage />
        </PageAnimationWrapper>
      )}
      {location.pathname === PATH.modelType && (
        <PageAnimationWrapper key={'modelType'}>
          <ModelTypePage />
        </PageAnimationWrapper>
      )}
      ...
    </AnimatePresence>
  );
}

<Router/> <Routes/> 또는, createBrouserRouter <Outlet/>을 통해 라우팅하면 사라지는 컴포넌트(페이지)를 감지할 수가없다.
<Router /> <Outlet /> 컴포넌트는 바뀌지 않고 그 내부의 children만 바뀌기 때문에 감지하기가 힘들다.
따라서 <AnimatePresence/> 컴포넌트의 children이 직접적으로 바뀔 수 있게 구현하였다.

AnimatePresence

<AnimatePresence/>location.pathname에 따라 바뀌는 컴포넌트를 감지하고,
언마운트 될 컴포넌트, 마운트 될 컴포넌트를 구분하여 렌더링하는 역할을 한다.

PageAnimationWrapper

렌더링이 되면 애니메이션을 실행하는 역할을 한다.

AnimatePresence

interface IAnimatePresence {
  children: ReactNode;
}

1. 언마운트 될 컴포넌트 감지

useRef를 이용해 참조한다.
아래에서 구체적으로 알아보자.

2. 언마운 될 컴포넌트, 마운트 될 컴포넌트 참조

언마운트 될 컴포넌트는 렌더링 직후에 기억하게 한다.
useEffect를 이용해 렌더링 이후에 작업을 수행할 수 있다.
즉, prev는 렌더링 직후 미리 업데이트 해 놓고, 다음 페이지로 이동 시 렌더링 전에 쓰는것이다.

3. 복제, 렌더링

const clonedElement = cloneElement(element, props, ...children)

언마운트 될 컴포넌트, 마운트 될 컴포넌트를 모두 복제한다.
isVisible props를 통해 컴포넌트를 구분하여 생성한다.
false라면 사라질 대상, true라면 보여질 대상이다.
A 에서 B로 페이지 이동을 한다면 A는 false, B는 true이다.
또, 사라질 대상은 onExitAnimationDone 콜백 함수를 props에 추가로 준다.

PageAnimationWrapper

interface IPageAnimationWrapper {
  onExitAnimationDone?: () => void;
  isVisible?: boolean;
}

애니메이션

isVisible에 따라 animate 메서드로 애니메이션을 실행시킨다.

false라면 onExitAnimationDone를 실행시킨다.

제거

onExitAnimationDone 함수는 <AnimatePresence/>를 강제로 렌더링 시킨다.
1. 렌더링할 컴포넌트 집합(useRef 이용) 에서 언마운트 대상을 제거한다.
2. forceRender 함수를 사용해 강제로 렌더링한다.

const [_, forceRender] = useState(0);

느낀점

useRef useEffect useState의 완벽한 이해가 필요하다 느꼈다.
처음에는 굉장히 막막했지만 시간을 투자하니 되긴 되더라.
뿌듯하다.😀

참고 블로그

https://jaeseokim.dev/React/Framer-Motion%EC%9D%98-AnimatePresence-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0/

https://itchallenger.tistory.com/911

1개의 댓글

comment-user-thumbnail
2023년 8월 6일

훌륭한 글 감사드립니다.

답글 달기