DEVELOP
article thumbnail

 

커스텀 훅으로 toast message를 추가하는 방법은 아래 포스팅 참고 ! https://cjy00n.tistory.com/210

 

[ React + Recoil ] ToastMessage 커스텀 훅 제작 (토스트메시지, useToastMessage)

Recoil으로 전역으로 상태를 관리하며 위처럼 토스트 메시지 띄우는 작업을 커스텀 훅을 만들어 구현해보고자 한다. 타임아웃을 기본값으로 3초를 설정하여, 생성 3초 후 자동으로 없어지도록 하

cjy00n.tistory.com


위 gif처럼 메시지가 생성될 때 fade-in, 메시지가 사라질 때 fade-out이 되도록 tailwind css에서 애니메이션을 정의하고 추가해줄 것이다.

메시지가 생성될 때 fade-in 효과를 주고,

삭제하기 0.5초 전 fade-out 애니메이션이 실행되도록 할 것이다.

로직은 다음과 같다.

  1. tailwind.config.js에 fadeIn, fadeOut 애니메이션을 정의한다.
  2. toastMessage type에 isRemoving 속성을 추가해준다. ( 삭제 “중”인 메시지를 뜻함)
  3. useToast를 수정한다.
    1. addToast 함수에서 새 toast를 생성하면서 isRemoving 속성을 false로 설정해주고, removeToast 함수 호출을 0.5초 빨리 한다.
    2. removeToast 함수에서 이 함수가 호출되면 해당 토스트의 isRemoving 속성을 true로 변경해주고, 0.5초 뒤 배열에서 해당 토스트를 삭제한다.
  4. toastMessageItem 컴포넌트에서 isRemoving 값에 따라, 이 값이 true이면 fade-out 애니메이션을 적용하고, false이면 fade-in 애니메이션을 적용하도록 한다.

tailwind 애니메이션 정의

▼ tailwind.config.js

// ... 
theme: {
  extend: {
    keyframes: {
      fadein: {
        "0%": {
          opacity: "0.5",
          transform: "translateY(-10px)",
        },
        "100%": {
          opacity: "1",
          transform: "translateY(0)",
        },
      },
      fadeout: {
        "0%": {
          opacity: "1",
        },
        "100%": {
          opacity: "0",
          transform: "translateY(-10px)",
        },
      },
    },
    animation: {
      fadein: "fadein 0.5s",
      fadeout: "fadeout 1s",
    },
  },
},
//...
};

tailwind css에서 애니메이션을 적용하기 위해서는 tailwind.config.js 파일을 수정해주어야 한다.

theme > extend > keyframes 에 fadein, fadeout을 정의한다.

fadein

  • 투명도 50%, y축이 -10px → 투명도 100%, y축이 0
  • 생성 시 위에서 아래로 내려오면서 선명해지도록 한다.

fadeout

  • 투명도 100% → 투명도 0%, y축이 -10px
  • 사라질 때 점점 투명해지면서 위쪽으로 올라가도록 한다.

그 다음에, 애니메이션을 정의하기 위해

theme > extend > animation에 fadeout, fadein을 정의한다.

fadein

  • 0.5초동안 실행된다.

fadeout

  • 1초동안 실행된다. ( 처음에 0.5초로 설정했으나, 어색한 부분이 있어 1초로 수정함)

해당 애니메이션이 필요한 컴포넌트의 클래스명에 animate-fadein , animate-fadeout 을 명시하여 사용할 수 있게 설정되었다.

toastMessage type 수정

▼ src/types/ToastMessage.ts

export default interface ToastMessage {
  id?: number;
  content: string;
  type?: "default" | "success" | "error";
  timeout?: number;
  isRemoving?: boolean; // 삭제"중"임을 의미하는 isRemoving 추가 
}

기존 ToastMessage type에 삭제”중”임을 의미하는 isRemoving을 추가한다.

useToast 수정

addToast 함수 수정

▼ src/hooks/useToast.tsx

const addToast = ({
  content,
  type = "default",
  timeout = 2000,
}: ToastMessage) => {
  const newToast = { id: Date.now(), isRemoving: false, content, type }; // isRemoving 추가
  setToasts((prev) => [...prev, newToast]);

  setTimeout(() => {
    removeToast(newToast.id);
  }, timeout - 500); // isRemoving 상태 0.5초
};

새 Toast를 생성하면서, isRemoving의 값을 false로 지정하여 생성한다. (newToast)

removeToast를 호출하는 시간을 timeout-500 ( 0.5초 빼기) 으로 수정하여, 0.5초동안 fadeout 애니메이션이 적용될 수 있도록 한다.

removeToast 함수 수정

▼ src/hooks/useToast.tsx

const removeToast = (id: number) => {
  setToasts((prev) =>
    prev.map((toast) =>
      toast.id === id ? { ...toast, isRemoving: true } : toast
    )
  );
  setTimeout(() => {
    setToasts((prev) => prev.filter((toast) => toast.id !== id));
  }, 500); // isRemoving 상태 0.5초 기다린 후 배열에서 삭제
};

removeToast 함수가 호출될 때 가장 먼저, 해당 토스트의 isRemoving을 true로 바꾼 뒤 setToast 해주어, 해당 토스트가 삭제”중”임을 의미하도록 한다.

0.5초 뒤에 toast 배열에서 해당 토스트를 삭제하고 setToast해주어 완전히 삭제되도록 한다.

⇒ 0.5초 동안은 삭제 예정이니까 애니메이션 실행해

⇒ 0.5초 후에는 진짜 삭제!

이런 느낌이다..

toastMessageItem 컴포넌트 수정

▼ src/components/Toast/ToastMessageContainer.tsx

{toasts &&
  toasts.map(({ content, type, id, isRemoving }) => (
    <ToastMessageItem
      content={content}
      type={type}
      key={"toast" + id}
      isRemoving={isRemoving} /* isRemoving 넘겨주기 */
    />
  ))}

먼저 , toastMessageItem 컴포넌트를 호출하는 toastMessageContainer 컴포넌트에서 isRemoving을 넘겨주도록 추가한다.

▼ src/components/Toast/ToastMessageItem.tsx

const ToastMessageItem = ({ type, content, isRemoving }: ToastMessage) => {
  return (
    <div className={`/* 다른 스타일 생략 */${isRemoving ? "animate-fadeout " : "animate-fadein "}}
    /* ... */ />

스타일을 지정해주는 className에서 isRemoving 값이 true이면 fadeout 애니메이션을 실행하고, false일 경우에는 fadeIn 애니메이션을 실행하도록 지정해준다.

isRemoving ? "animate-fadeout " : "animate-fadein "

연속되어 메시지를 생성할 때 엄청 자연스럽지는 않지만,, 토스트 메시지 애니메이션 적용하기 성공!

profile

DEVELOP

@JUNGY00N