Skip to content

Commit

Permalink
perf: 댓글이 보여지는 부분에 memo를 적용하라 (#609)
Browse files Browse the repository at this point in the history
- 상세 페이지의 댓글에 React.memo를 적용하여 성능 최적화
  • Loading branch information
saseungmin authored Feb 5, 2023
1 parent d14bde2 commit 58e7596
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 45 deletions.
13 changes: 6 additions & 7 deletions src/components/detail/CommentView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,30 @@ describe('CommentView', () => {

const renderCommentView = () => render((
<CommentView
user={given.user}
userUid={given.userUid}
comment={COMMENT_FIXTURE}
onRemove={handleRemove}
/>
));

context('댓글 작성자인 경우', () => {
given('user', () => PROFILE_FIXTURE);
given('userUid', () => PROFILE_FIXTURE.uid);

describe('삭제 버튼을 클릭한다', () => {
it('클릭 이벤트가 발생해야만 한다', () => {
renderCommentView();

fireEvent.click(screen.getByText('삭제'));

expect(handleRemove).toHaveBeenCalledWith(COMMENT_FIXTURE.commentId);
expect(handleRemove).toHaveBeenCalledWith({
commentId: COMMENT_FIXTURE.commentId, groupId: COMMENT_FIXTURE.groupId,
});
});
});
});

context('댓글 작성자가 아닌 경우', () => {
given('user', () => ({
...PROFILE_FIXTURE,
uid: '1',
}));
given('userUid', () => '1');

it('삭제 버튼이 나타나지 않아야 한다', () => {
const { container } = renderCommentView();
Expand Down
24 changes: 16 additions & 8 deletions src/components/detail/CommentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import styled from '@emotion/styled';
import dayjs from 'dayjs';

import { Profile } from '@/models/auth';
import { DeleteCommentForm } from '@/hooks/api/comment/useDeleteComment';
import { Comment } from '@/models/group';
import { body1Font, body2Font, subtitle1Font } from '@/styles/fontStyles';
import { filteredWithSanitizeHtml } from '@/utils/filter';
Expand All @@ -15,17 +15,25 @@ import ProfileImage from '../common/ProfileImage';

interface Props {
comment: Comment;
user: Profile | null;
onRemove: (commentId: string) => void;
userUid: string | undefined;
onRemove: ({ commentId, groupId }: DeleteCommentForm) => void;
}

const commentViewPropsAreEqual = (prevProps: Props, nextProps: Props) => (
prevProps.comment.commentId === nextProps.comment.commentId
&& prevProps.userUid === nextProps.userUid
&& prevProps.onRemove === nextProps.onRemove
&& prevProps.comment.writer.uid === nextProps.comment.writer.uid
&& prevProps.comment.groupId === nextProps.comment.groupId
);

function CommentView({
comment, user, onRemove,
comment, userUid, onRemove,
}: Props, ref: ForwardedRef<HTMLDivElement>): ReactElement {
const {
writer, content, createdAt, commentId,
content, createdAt, commentId, groupId, writer,
} = comment;
const isWriter = user?.uid === writer.uid;
const isWriter = userUid === writer.uid;

return (
<CommentViewWrapper ref={ref}>
Expand All @@ -40,7 +48,7 @@ function CommentView({
{isWriter && (
<RemoveCommentButton
type="button"
onClick={() => onRemove(commentId)}
onClick={() => onRemove({ commentId, groupId })}
>
삭제
</RemoveCommentButton>
Expand All @@ -52,7 +60,7 @@ function CommentView({
);
}

export default memo(forwardRef<HTMLDivElement, Props>(CommentView));
export default memo(forwardRef<HTMLDivElement, Props>(CommentView), commentViewPropsAreEqual);

const CommentStatus = styled.div`
display: flex;
Expand Down
13 changes: 10 additions & 3 deletions src/components/detail/CommentsView.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';

import useDeleteComment from '@/hooks/api/comment/useDeleteComment';
import useInfiniteFetchComments from '@/hooks/api/comment/useInfiniteFetchComments';

import COMMENT_FIXTURE from '../../../fixtures/comment';
Expand All @@ -8,6 +9,7 @@ import PROFILE_FIXTURE from '../../../fixtures/profile';
import CommentsView from './CommentsView';

jest.mock('@/hooks/api/comment/useInfiniteFetchComments');
jest.mock('@/hooks/api/comment/useDeleteComment');

describe('CommentsView', () => {
const handleRemove = jest.fn();
Expand All @@ -27,13 +29,16 @@ describe('CommentsView', () => {
lastItemRef,
},
}));

(useDeleteComment as jest.Mock).mockImplementation(() => ({
mutate: handleRemove,
}));
});

const renderCommentsView = () => render((
<CommentsView
perPage={15}
user={PROFILE_FIXTURE}
onRemove={handleRemove}
userUid={PROFILE_FIXTURE.uid}
/>
));

Expand All @@ -53,7 +58,9 @@ describe('CommentsView', () => {

fireEvent.click(screen.getByText('삭제'));

expect(handleRemove).toHaveBeenCalledWith(COMMENT_FIXTURE.commentId);
expect(handleRemove).toHaveBeenCalledWith({
commentId: COMMENT_FIXTURE.commentId, groupId: COMMENT_FIXTURE.groupId,
});
});
});
});
20 changes: 12 additions & 8 deletions src/components/detail/CommentsView.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { memo, ReactElement } from 'react';
import { ReactElement, useCallback } from 'react';

import styled from '@emotion/styled';

import useDeleteComment, { DeleteCommentForm } from '@/hooks/api/comment/useDeleteComment';
import useInfiniteFetchComments from '@/hooks/api/comment/useInfiniteFetchComments';
import { Profile } from '@/models/auth';
import { targetFalseThenValue } from '@/utils/utils';

import CommentSkeletonLoader from './CommentSkeletonLoader';
import CommentView from './CommentView';

interface Props {
user: Profile | null;
userUid: string | undefined;
perPage: number;
onRemove: (commentId: string) => void;
}

function CommentsView({ user, perPage, onRemove }: Props): ReactElement {
function CommentsView({ userUid, perPage }: Props): ReactElement {
const { query: { data, isFetchingNextPage }, refState } = useInfiniteFetchComments({
perPage,
});
const { mutate: deleteCommentMutate } = useDeleteComment(perPage);

const comments = data.pages;

const onRemoveComment = useCallback((
commentForm: DeleteCommentForm,
) => deleteCommentMutate(commentForm), []);

return (
<CommentsViewWrapper>
{comments.map(({ items }) => (
Expand All @@ -31,8 +35,8 @@ function CommentsView({ user, perPage, onRemove }: Props): ReactElement {
return (
<CommentView
key={comment.commentId}
user={user}
onRemove={onRemove}
userUid={userUid}
onRemove={onRemoveComment}
comment={comment}
ref={targetFalseThenValue(!isLastItem)(refState.lastItemRef)}
/>
Expand All @@ -46,7 +50,7 @@ function CommentsView({ user, perPage, onRemove }: Props): ReactElement {
);
}

export default memo(CommentsView);
export default CommentsView;

const CommentsViewWrapper = styled.div`
& > div:last-of-type {
Expand Down
4 changes: 2 additions & 2 deletions src/components/home/TagsBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, ReactElement } from 'react';
import { ReactElement } from 'react';

import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
Expand Down Expand Up @@ -29,7 +29,7 @@ function TagsBar(): ReactElement {
);
}

export default memo(TagsBar);
export default TagsBar;

const TagsWrapper = styled.div`
height: 36px;
Expand Down
6 changes: 2 additions & 4 deletions src/components/myInfo/SettingForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
memo, ReactElement, useEffect, useState,
} from 'react';
import { ReactElement, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useEffectOnce, useUpdateEffect } from 'react-use';

Expand Down Expand Up @@ -133,7 +131,7 @@ function SettingForm({
);
}

export default memo(SettingForm);
export default SettingForm;

const MemberWithdrawalButton = styled(Button)`
color: ${({ theme }) => theme.accent6};
Expand Down
6 changes: 3 additions & 3 deletions src/containers/detail/CommentsContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jest.mock('@/hooks/api/comment/useAddComment');
jest.mock('next/router', () => ({
useRouter: jest.fn().mockImplementation(() => ({
query: {
id: '1',
id: COMMENT_FIXTURE.groupId,
},
})),
}));
Expand Down Expand Up @@ -90,7 +90,7 @@ describe('CommentsContainer', () => {
expect(mutate).toHaveBeenCalledWith({
content: commentValue,
writer,
groupId: '1',
groupId: COMMENT_FIXTURE.groupId,
});
});

Expand All @@ -102,7 +102,7 @@ describe('CommentsContainer', () => {

expect(mutate).toHaveBeenCalledWith({
commentId: COMMENT_FIXTURE.commentId,
groupId: '1',
groupId: COMMENT_FIXTURE.groupId,
});
});
});
Expand Down
11 changes: 2 additions & 9 deletions src/containers/detail/CommentsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import CommentForm from '@/components/detail/CommentForm';
import CommentSkeletonLoader from '@/components/detail/CommentSkeletonLoader';
import useFetchUserProfile from '@/hooks/api/auth/useFetchUserProfile';
import useAddComment from '@/hooks/api/comment/useAddComment';
import useDeleteComment from '@/hooks/api/comment/useDeleteComment';
import useFetchCommentCount from '@/hooks/api/comment/useFetchCommentCount';
import { GroupQuery } from '@/models';
import { CommentFields } from '@/models/group';
Expand All @@ -28,16 +27,11 @@ function CommentsContainer(): ReactElement {
const { data: user } = useFetchUserProfile();
const { data: commentCount } = useFetchCommentCount(groupId);
const { mutate: addCommentMutate } = useAddComment(perPage);
const { mutate: deleteCommentMutate } = useDeleteComment(perPage);

const onSubmit = useCallback((commentForm: CommentFields) => addCommentMutate({
...commentForm,
groupId,
}), [groupId, addCommentMutate]);

const onRemoveComment = useCallback((commentId: string) => deleteCommentMutate({
commentId, groupId,
}), [groupId, deleteCommentMutate]);
}), [groupId]);

return (
<>
Expand All @@ -50,9 +44,8 @@ function CommentsContainer(): ReactElement {
<ErrorBoundary>
<Suspense fallback={<CommentSkeletonLoader />}>
<CommentsView
user={user}
userUid={user?.uid}
perPage={perPage}
onRemove={onRemoveComment}
/>
</Suspense>
</ErrorBoundary>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/api/comment/useDeleteComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useCatchFirestoreErrorWithToast from '../useCatchFirestoreErrorWithToast'

import { deleteCommentQueryData } from './queryDataUtils';

type DeleteCommentForm = {
export type DeleteCommentForm = {
commentId: string;
groupId: string;
};
Expand Down

1 comment on commit 58e7596

@vercel
Copy link

@vercel vercel bot commented on 58e7596 Feb 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.