react-toastify 적용기

먼지·2022년 11월 27일
15
post-thumbnail

post, put, delete 등 api를 사용하는 곳에서 항상 alert로 알림을 띄었는데 좀 더 직관적이고 이쁘게 알림을 표시하고 싶어서 이번에 처음으로 react notification library 중 유명한 react toastify를 사용해 보려고 한다!

Installation

$ npm install --save react-toastify
$ yarn add react-toastify

Try

import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function App() {
  const notify = () => toast('toastify test!');

  return (
    <>
      <button onClick={notify}>
        test
      </button>
      <ToastContainer
        position="top-right" // 알람 위치 지정
        autoClose={3000} // 자동 off 시간
        hideProgressBar={false} // 진행시간바 숨김
        closeOnClick // 클릭으로 알람 닫기
        rtl={false} // 알림 좌우 반전
        pauseOnFocusLoss // 화면을 벗어나면 알람 정지
        draggable // 드래그 가능
        pauseOnHover // 마우스를 올리면 알람 정지
        theme="light"
        // limit={1} // 알람 개수 제한
      />
    </>
  );
}

export default App;

test 버튼을 클릭하면 내가 position에 지정한 대로 알림 창이 뜬닷. 나중에 스타일도 변경해 보고 싶다 How to style

게시글 작성에 적용

방식은 대충 알겠는데 그럼 비동기 함수 등에선 어떻게 사용해야 할까? 문서에 여러 기능이 있는데 일단 내가 찾는 부분인 것 같아 한 번 게시글을 작성하는 부분에 적용해 보려고 한다. (Handling promises)

나도 백엔드쪽 지식이 부족하고 백엔드님이 서버 에러를 400, 500 이런 상태코드에 맞게? 안 보내주시고 json 형태로 에러 코드와 메시지를 보내주셔서 이거를 throw, throw new Error, reject 등 어떻게 보내고 그 에러데이터를 받아서 추출할 수 있는지 등 전부터 시간이 좀 걸렸다ㅜ

여전히 허접하지만 일단 간단하게 작성

// modules/board/api.ts
export const createPostRequest = async (token: string, data: CreateBoard) => {
  headers.set('Authorization', token);
  const json = await (
    await fetch(`${BOARD_API_END_POINT}/create`, {
      method: 'POST',
      body: JSON.stringify(data),
      headers,
    })
  ).json();
  // if (!json)
  if (json.error || json.errorCode) {
    // throw json;
    throw new Error(`${json.error || json.message}`);
  }
  return json;
};

// pages/WritePost.tsx
export default function WritePost() {
  ...
  const handleCreateBoard = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
	...
    createPostRequest(currentUser.accessToken, data)
      .then((res) => {
        toast.success('게시글 작성 완료 👌');
        navigate(`/board/${res.boardId}`);
      })
      .catch((err) => {
        toast.error(err.message); // throw new Error(string) => err.message vs err.toString()
      });
    
    // toast
    //   .promise(() => createPostRequest('currentUser.accessToken', data), {
    //     pending: '게시글 작성 진행 중',
    //     success: '게시글 작성 완료 👌',
    //     error: '게시글 작성 실패 🤯',
    //   })
    //   .then((res) => {
    //     console.log('WritePost createBoardRequest res :', res);
    //     // navigate(`/board/${res.boardId}`);
    //   })
    //   .catch((err) => {
    //     console.log('WritePost createBoardRequest err :', err);
    //     // const [code, message] = err.message.split('/'); // '500/OOO' || 'X_XX/OO'
    //   });
  };

handleCreateBoard 함수는 등록 버튼을 눌렀을 때 실행되는 함수로, 밑에 주석 처리해 놓은 Handling promises 문서 예시를 보고 따라한 코드는 메시지가 한정적이라? 그냥 thrw new Error로 서버에서 전송되는 메시지 객체를 문자열로 변환 후 toast.error & success로 보여줬다.

작성 완료

작성 실패

순서 문제?

만약 토큰 만료 에러일 때 toast.error 알림창이 먼저 뜨고 windoc.confirm 순차적으로 동작할 줄 알았는데 confirm 메서드가 먼저 실행되고 사용자 입력이 끝났을 때 toast가 나온다.

  const handleCreateBoard = (e: React.FormEvent<HTMLFormElement>) => {
    ...
      .catch((err) => {
        const [code, message] = err.message.split('-'); // EXPIRE_ACCESS_TOKEN-만료된 토큰입니다. 다시 로그인해주세요
        toast.error(message); // throw new Error(string) => err.message xx || err.toString() Error: xx
        // 만료된 토큰일 때
        // ...
        window.confirm('로그인하시겠습니까?');

내가 원하던 방식은 아니지만 비슷하게 작동하는 방법을 찾음! (Define callback)

이렇게 작성하면 메시지가 나오고 알림이 닫힐 때 onClose에 등록된 함수가 실행된다. 그래서 만약 만료된 토큰에 대한 에러면, 먼저 만료된 토큰입니다.. 어찌고 메시지가 뜨고 등록된 시간이 지나거나 사용자가 알림을 닫으면 '로그인하시겠습니까?' confirm 창이 뜨고 불린 값에 따라 로직을 처리했다. 그 외의 에러는 그냥 에러 메시지만 보여주게 작성함

  ...
  const handleCreateBoard = (e: React.FormEvent<HTMLFormElement>) => {
    ...
      .catch((err) => {
        const [code, message] = err.message.split('-');
        if (code === 'EXPIRE_ACCESS_TOKEN') {
          toast(message, {
            onClose: () => {
              if (window.confirm('로그인하시겠습니까?')) {
                resetUser(); // 로그아웃
                // 작성중인게시글데이터보관
                navigate('/register', {
                  state: { path: '/board/new', data },
                });
              } else {
                navigate(-1);
              }
            },
          });
        } else {
          toast.error(message);
        }
      });
  };

참고
react-toastify - npm
React-Toastify - GitHub Pages
[React.js] react-toastify 란? / 사용법

profile
꾸준히 자유롭게 즐겁게

0개의 댓글