[Recoil] Recoil 사용해서 전역으로 모달 관리, unmount 시 animation 적용하기

Chloe K·2023년 2월 13일
1
post-thumbnail

리액트 Mount & Unmount

리액트에서 마운트 & 언마운트를 불리언 값인 true or false로 관리했다. true이면 컴포넌트를 보여주고(mount), false이면 컴포넌트가 사라지는(unmount) 로직이다.
CSS의 animation 기능을 사용하여 컴포넌트에 효과를 넣으면 mount시에는 적용이 되지만 unmount 시에는 애니메이션 효과가 적용이 되지 않고 그냥 사라져버린다.

🔨 이러한 문제를 해결하기 위해서

  • useAnimation 커스텀 훅을 만들거나
  • useEffect 훅을 사용해서 animation 적용을 핸들링
  • state(boolean 값)를 통한 핸들링

recoil을 통해서 전역으로 상태관리 하면서 state를 통해서 핸들링하는 방법을 적용시켜보려고 한다.

├── src
│   ├── atom.js
│   ├── MyComponent.jsx
│   └── Modal.jsx
└── ...
// atom.js

export const modalState = atom({
  key: "modalState",
  default: {
    isOpen: false,
    animate: false
  }
})
// MyComponent.jsx

const MyComponent = () => {
  const { isOpen } = useRecoilValue(openState);
  const setIsOpen = useSetRecoilState(openState);
  // const [isOpen, setIsOpen] = useRecoilState(openState) 로 작성해도 OK!

  const openModal = () => {
    setIsOpen((prev) => {
      return { ...prev, isOpen: true, animate: true };
    });
  };

  return (
    <>
      <button onClick={openModal}>메뉴</button>
      <Container>{isOpen && <Nav />}</Container>
    </>
  );
};

export default MyComponent;
// Modal.jsx

import React from "react";
import styled, { keyframes } from "styled-components";
import { useSetRecoilState, useRecoilValue } from "recoil";
import { modalState } from "./atom";

const Nav = () => {
  const { animate } = useRecoilValue(modalState);
  const setIsOpen = useSetRecoilState(modalState);

  const onClose = () => {
    setIsOpen((prev) => {
      return { ...prev, animate: false };
    });
    setTimeout(() => {
      setIsOpen((prev) => {
        return { ...prev, isOpen: false };
      });
    }, 350);
  };

  // aniMode가 꼭 필요함!! --> why? aniMode가 없으면 닫기 버튼을 클릭했을 때 setTimeout 함수로 인해서 slideOut 애니메이션 적용이 안된다.
  // ⭐️animation 시간만큼 unmount -> 즉 false가 되는걸 늦춰준다!!!!⭐️

  return (
    <Wrapper aniMode={animate}>
      Hi this is Modal.
      <button onClick={onClose}>close</button>
    </Wrapper>
  );
};

export default Nav;

// styled-components
const slideIn = keyframes`
from {
  transform: translateY(-100%);
}
to {
  transform: translateY(0);
}
`;

const slideOut = keyframes`

from {
  transform: translateY(0);
}
to {
  transform: translateY(-100%);
}
`;

const Wrapper = styled.div`
  width: 200px;
  height: 200px;
  position: absolute;
  background-color: lightgray;
  animation: ${({ aniMode }) => (aniMode ? slideIn : slideOut)} 0.35s
    ease-in-out;
`;
profile
Frontend Developer

1개의 댓글

comment-user-thumbnail
2023년 5월 29일

감사합니다 많은 참고가 되었습니다

답글 달기