SEB_FE_44/Main_Project

리팩토링 - queries 분리

언젠간코딩잘함 2023. 8. 9. 16:28

리팩토링 - queries 분리

 

폴더구조, path alias 설정이 끝났으니 무거웠던 컴포넌트들의 코드를 분리시켜 주어야 한다. 폴더구조를 더 개선하기 위해서도 필요하고 성능 향상등 다양한 이유로 아무튼 해야 한다. 우리 프로젝트의 api 는 분리되어 있는 반면에 리액트 쿼리는 그렇지 않기 때문에, 가장 먼저 수정해야겠다고 생각해서 useQuery, useMutation 들을 분리시킬 것이다!

 

 

이전 Bookmark 컴포넌트

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { BsHeart, BsHeartFill } from 'react-icons/bs';
import { useNavigate } from 'react-router-dom';
import { GetIsBookmark, PostBookmark } from '@/api/api';
import BookmarkLoading from '@/components/mediaDetail/bookmark/BookmarkLoading';
import { S_IconWrapper } from '@/styles/style';
import useIsLoggedIn from '@/utils/isLoggedIn';
import { notifyError, notifyWithIcon } from '@/utils/notify';

function Bookmark({ contentId }: { contentId: string }) {
  const queryClient = useQueryClient();
  const isLoggedIn = useIsLoggedIn();
  const navigate = useNavigate();

  if (!isLoggedIn) {
    return (
      <S_IconWrapper>
        <div>
          <BsHeart
            color="white"
            size="35"
            onClick={() => notifyError('로그인 후 이용 가능합니다')}
          />
          <p>찜</p>
        </div>
      </S_IconWrapper>
    );
  }

  const { isLoading, data, isSuccess, error } = useQuery(
    ['isBookmarked', contentId],
    () => GetIsBookmark(contentId),
    {
      enabled: isLoggedIn,
    }
  );

  const BookmarkMutation = useMutation({
    mutationFn: (contentId: string) => PostBookmark(contentId),
    onSuccess: () => {
      if (!data) {
        notifyWithIcon('찜 완료!', '❤️');
      } else {
        notifyWithIcon('찜 취소..', '🤍');
      }
      queryClient.invalidateQueries(['isBookmarked', contentId]);
      queryClient.invalidateQueries(['userContents']);
    },
  });

  const handleBookmark = () => {
    if (!BookmarkMutation.isLoading) {
      BookmarkMutation.mutate(contentId);
    }
  };

  if (isLoading) {
    return <BookmarkLoading />;
  }

  if (error instanceof AxiosError) {
    if (!error.status && error.code === 'ERR_NETWORK') navigate('/error');
  }

  if (isSuccess) {
    return (
      <S_IconWrapper>
        <div>
          {data ? (
            <BsHeartFill
              color="white"
              size="34"
              className="isTrue"
              onClick={handleBookmark}
            />
          ) : (
            <BsHeart color="white" size="35" onClick={handleBookmark} />
          )}
          <p className={data ? 'isTrue' : ''}>찜</p>
        </div>
      </S_IconWrapper>
    );
  }
}

export default Bookmark;

 

지금은 북마크 버튼을 렌더링 해주고, api 요청을 주고받고..

위 코드에서 useQuery 와 useMutation 을 뽑아서 새로운 컴포넌트에 옮겨주고 값만 가져오게 수정하자.

 

먼저 useIsBookmarkQuery.tsx 컴포넌트를 새로 만들어 주었다.

 

import { useQuery } from '@tanstack/react-query';
import { GetIsBookmark } from '@/api/api';

const useIsBookmarkQuery = (contentId: string) => {
  return useQuery(['isBookmarked', contentId], () => GetIsBookmark(contentId));
};

export default useIsBookmarkQuery;

 

contentId 만 받아서 똑같이 api 요청을 보내준다.

마찬가지로 useBookmarkMutation.tsx 컴포넌트도 만들어줬다.

 

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { PostBookmark } from '@/api/api';
import { notifyWithIcon } from '@/utils/notify';

const useBookmarkMutation = (contentId: string, data: boolean) => {
  const queryClient = useQueryClient();
  const bookmarkMutation = useMutation({
    mutationFn: () => PostBookmark(contentId),
    onSuccess: () => {
      if (!data) {
        notifyWithIcon('찜 완료!', '❤️');
      } else {
        notifyWithIcon('찜 취소..', '🤍');
      }
      queryClient.invalidateQueries(['isBookmarked', contentId]);
      queryClient.invalidateQueries(['userContents']);
    },
  });
  return bookmarkMutation;
};

export default useBookmarkMutation;

 

 

이후 Bookmark 컴포넌트

import { AxiosError } from 'axios';
import { BsHeart, BsHeartFill } from 'react-icons/bs';
import { useNavigate } from 'react-router-dom';
import BookmarkLoading from '@/components/mediaDetail/bookmark/BookmarkLoading';
import useBookmarkMutation from '@/queries/mediaDetail/useBookmarkMutation';
import useIsBookmarkQuery from '@/queries/mediaDetail/useIsBookmarkQuery';
import { S_IconWrapper } from '@/styles/style';
import useIsLoggedIn from '@/utils/isLoggedIn';
import { notifyError } from '@/utils/notify';

function Bookmark({ contentId }: { contentId: string }) {
  const isLoggedIn = useIsLoggedIn();
  const navigate = useNavigate();

  if (!isLoggedIn) {
    return (
      <S_IconWrapper>
        <div>
          <BsHeart
            color="white"
            size="35"
            onClick={() => notifyError('로그인 후 이용 가능합니다')}
          />
          <p>찜</p>
        </div>
      </S_IconWrapper>
    );
  }

  const { isLoading, data, isSuccess, error } = useIsBookmarkQuery(contentId);
  const bookmarkMutation = useBookmarkMutation(contentId, data);

  const handleBookmark = () => {
    if (!bookmarkMutation.isLoading) {
      bookmarkMutation.mutate();
    }
  };

  if (isLoading) {
    return <BookmarkLoading />;
  }

  if (error instanceof AxiosError) {
    navigate('/error');
  }

  if (isSuccess) {
    return (
      <S_IconWrapper>
        <div>
          {data ? (
            <BsHeartFill
              color="white"
              size="34"
              className="isTrue"
              onClick={handleBookmark}
            />
          ) : (
            <BsHeart color="white" size="35" onClick={handleBookmark} />
          )}
          <p className={data ? 'isTrue' : ''}>찜</p>
        </div>
      </S_IconWrapper>
    );
  }
}

export default Bookmark;

 

20줄도 안 줄은 것 같은데 아무튼 만족스럽다. 이런 식으로 다른 파일들도 리액트 쿼리를 분리해야지..