Skip to content

Commit

Permalink
feat: 리뷰 좋아요 버튼 컴포넌트 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
xodms0309 committed Nov 1, 2023
1 parent 4056bc5 commit c92e575
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Text, Button, useTheme } from '@fun-eat/design-system';
import { useState } from 'react';
import styled from 'styled-components';

import { SvgIcon } from '@/components/Common';
import { useTimeout } from '@/hooks/common';
import { useReviewFavoriteMutation } from '@/hooks/queries/review';

interface ReviewFavoriteButtonProps {
productId: number;
reviewId: number;
favorite: boolean;
favoriteCount: number;
}

const ReviewFavoriteButton = ({ productId, reviewId, favorite, favoriteCount }: ReviewFavoriteButtonProps) => {
const theme = useTheme();

const initialFavoriteState = {
isFavorite: favorite,
currentFavoriteCount: favoriteCount,
};

const [favoriteInfo, setFavoriteInfo] = useState(initialFavoriteState);
const { isFavorite, currentFavoriteCount } = favoriteInfo;

const { mutate } = useReviewFavoriteMutation(productId, reviewId);

const handleToggleFavorite = async () => {
setFavoriteInfo((prev) => ({
isFavorite: !prev.isFavorite,
currentFavoriteCount: isFavorite ? prev.currentFavoriteCount - 1 : prev.currentFavoriteCount + 1,
}));

mutate(
{ favorite: !isFavorite },
{
onError: () => {
setFavoriteInfo(initialFavoriteState);
},
}
);
};

const [debouncedToggleFavorite] = useTimeout(handleToggleFavorite, 200);

return (
<FavoriteButton
type="button"
variant="transparent"
onClick={debouncedToggleFavorite}
aria-label={`좋아요 ${currentFavoriteCount}개`}
>
<SvgIcon variant={isFavorite ? 'favoriteFilled' : 'favorite'} color={isFavorite ? 'red' : theme.colors.gray4} />
<Text as="span" weight="bold">
{currentFavoriteCount}
</Text>
</FavoriteButton>
);
};

export default ReviewFavoriteButton;

const FavoriteButton = styled(Button)`
display: flex;
align-items: center;
padding: 0;
column-gap: 8px;
`;
59 changes: 7 additions & 52 deletions frontend/src/components/Review/ReviewItem/ReviewItem.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Badge, Button, Text, useTheme } from '@fun-eat/design-system';
import { memo, useState } from 'react';
import { Badge, Text, useTheme } from '@fun-eat/design-system';
import { memo } from 'react';
import styled from 'styled-components';

import ReviewFavoriteButton from '../ReviewFavoriteButton/ReviewFavoriteButton';

import { SvgIcon, TagList } from '@/components/Common';
import { useTimeout } from '@/hooks/common';
import { useToastActionContext } from '@/hooks/context';
import { useReviewFavoriteMutation } from '@/hooks/queries/review';
import type { Review } from '@/types/review';
import { getRelativeDate } from '@/utils/date';

Expand All @@ -15,37 +14,10 @@ interface ReviewItemProps {
}

const ReviewItem = ({ productId, review }: ReviewItemProps) => {
const { id, userName, profileImage, image, rating, tags, content, createdAt, rebuy, favoriteCount, favorite } =
review;
const [isFavorite, setIsFavorite] = useState(favorite);
const [currentFavoriteCount, setCurrentFavoriteCount] = useState(favoriteCount);

const { toast } = useToastActionContext();
const { mutate } = useReviewFavoriteMutation(productId, id);

const theme = useTheme();

const handleToggleFavorite = async () => {
mutate(
{ favorite: !isFavorite },
{
onSuccess: () => {
setIsFavorite((prev) => !prev);
setCurrentFavoriteCount((prev) => (isFavorite ? prev - 1 : prev + 1));
},
onError: (error) => {
if (error instanceof Error) {
toast.error(error.message);
return;
}

toast.error('리뷰 좋아요를 다시 시도해주세요.');
},
}
);
};

const [debouncedToggleFavorite] = useTimeout(handleToggleFavorite, 200);
const { id, userName, profileImage, image, rating, tags, content, createdAt, rebuy, favorite, favoriteCount } =
review;

return (
<ReviewItemContainer>
Expand Down Expand Up @@ -79,17 +51,7 @@ const ReviewItem = ({ productId, review }: ReviewItemProps) => {
{image && <ReviewImage src={image} height={150} alt={`${userName}의 리뷰`} />}
<TagList tags={tags} />
<ReviewContent>{content}</ReviewContent>
<FavoriteButton
type="button"
variant="transparent"
onClick={debouncedToggleFavorite}
aria-label={`좋아요 ${favoriteCount}개`}
>
<SvgIcon variant={isFavorite ? 'favoriteFilled' : 'favorite'} color={isFavorite ? 'red' : theme.colors.gray4} />
<Text as="span" weight="bold">
{currentFavoriteCount}
</Text>
</FavoriteButton>
<ReviewFavoriteButton productId={productId} reviewId={id} favorite={favorite} favoriteCount={favoriteCount} />
</ReviewItemContainer>
);
};
Expand Down Expand Up @@ -141,10 +103,3 @@ const ReviewImage = styled.img`
const ReviewContent = styled(Text)`
white-space: pre-wrap;
`;

const FavoriteButton = styled(Button)`
display: flex;
align-items: center;
padding: 0;
column-gap: 8px;
`;
1 change: 1 addition & 0 deletions frontend/src/components/Review/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { default as ReviewTagItem } from './ReviewTagItem/ReviewTagItem';
export { default as ReviewTagList } from './ReviewTagList/ReviewTagList';
export { default as ReviewRegisterForm } from './ReviewRegisterForm/ReviewRegisterForm';
export { default as BestReviewItem } from './BestReviewItem/BestReviewItem';
export { default as ReviewFavoriteButton } from './ReviewFavoriteButton/ReviewFavoriteButton';

0 comments on commit c92e575

Please sign in to comment.