Skip to content

Commit 76188d7

Browse files
authored
[#25] 사용자가이드 단계 이동시 헤더 내 모든 요소 리렌더링 개선 (#37)
* 🎨 style: 도움말버튼 컴포넌트 분리 (HelpButton) * 🔨 refactor: 코치마크 단계 이동시 도움말버튼 리렌더링 개선 * 🔨 refactor: 헤더 상단 버튼 각각 메모이제이션 * 🔨 refactor: 코치마크 4단계로 이동하거나 4단계 였던 경우에만 HeaderButtons 리렌더링 되도록 메모이제이션 * 🔨 refactor: 워크스페이스 헤더 리렌더링 개선 완료 * 🐛 fix: 대소문자 오타 수정
1 parent 8e6a057 commit 76188d7

File tree

11 files changed

+85
-45
lines changed

11 files changed

+85
-45
lines changed

apps/client/src/entities/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export { ImageTagModalImg } from './workspace/ImageTagModalImg/ImageTagModalImg'
1919
export { ImageTagModalButton } from './workspace/ImageTagModalButton/ImageTagModalButton';
2020
export { ImageTagModalListItem } from './workspace/ImageTagModalListItem/ImageTagModalListItem';
2121
export { CodeExportButton } from './workspace/CodeExportButton/CodeExportButton';
22+
export { HelpButton } from './workspace/HelpButton/HelpButton';

apps/client/src/entities/workspace/CodeExportButton/CodeExportButton.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Spinner } from '@/shared/ui';
22
import { exportPreviewHtml } from '@/shared/utils';
33
import toast from 'react-hot-toast';
4-
import { useState } from 'react';
4+
import { memo, useState } from 'react';
55

6-
export const CodeExportButton = () => {
6+
export const CodeExportButton = memo(() => {
77
const [isLoading, setIsLoading] = useState(false);
88

99
const handleClick = () => {
@@ -32,4 +32,4 @@ export const CodeExportButton = () => {
3232
)}
3333
</button>
3434
);
35-
};
35+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Question from '@/shared/assets/question.svg?react';
2+
import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
3+
import { memo } from 'react';
4+
import { useShallow } from 'zustand/react/shallow';
5+
6+
export const HelpButton = memo(() => {
7+
const { openCoachMark } = useCoachMarkStore(
8+
useShallow((state) => ({
9+
openCoachMark: state.openCoachMark,
10+
}))
11+
);
12+
13+
return (
14+
<button
15+
className="text-medium-rg hover flex items-center gap-1 text-gray-300 hover:text-gray-500"
16+
onClick={openCoachMark}
17+
aria-label="도움말 버튼"
18+
>
19+
도움말 <Question />
20+
</button>
21+
);
22+
});

apps/client/src/entities/workspace/RedoButton/RedoButton.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { CircleButton } from '@/shared/ui';
22
import RightArrow from '@/shared/assets/arrow_right.svg?react';
33
import { useWorkspaceStore } from '@/shared/store';
4+
import { memo } from 'react';
45

56
/**
67
* @description
78
* 워크스페이스 캔버스에서 redo 기능을 실행시키는 버튼입니다.
89
*/
9-
export const RedoButton = () => {
10+
export const RedoButton = memo(() => {
1011
const { workspace } = useWorkspaceStore();
1112

1213
const handleRedo = () => {
@@ -20,4 +21,4 @@ export const RedoButton = () => {
2021
<RightArrow />
2122
</CircleButton>
2223
);
23-
};
24+
});

apps/client/src/entities/workspace/SaveButton/SaveButton.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import { cssStyleToolboxConfig } from '@/shared/blockly';
88
import toast from 'react-hot-toast';
99
import { useParams } from 'react-router-dom';
1010
import { useSaveWorkspace } from '@/shared/hooks';
11-
import { useState } from 'react';
11+
import { memo, useState } from 'react';
1212

1313
/**
1414
*
1515
* @description
1616
* Workspace 상태를 저장하는 버튼입니다.
1717
* 저장 항목 : css 속성, 캔버스 블록 상태, css class 블록, css 리셋 여부, 미리보기 썸네일
1818
*/
19-
export const SaveButton = () => {
19+
export const SaveButton = memo(() => {
2020
const workspaceId = useParams().workspaceId as string;
2121
const { mutate: saveWorkspace, isPending } = useSaveWorkspace(workspaceId);
2222
const totalCssPropertyObj = useCssPropsStore((state) => state.totalCssPropertyObj);
@@ -63,4 +63,4 @@ export const SaveButton = () => {
6363
</button>
6464
</>
6565
);
66-
};
66+
});

apps/client/src/entities/workspace/UndoButton/UndoButton.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { CircleButton } from '@/shared/ui';
22
import LeftArrow from '@/shared/assets/arrow_left.svg?react';
33
import { useWorkspaceStore } from '@/shared/store';
4+
import { memo } from 'react';
45

56
/**
67
*
78
* @description
89
* 워크스페이스 캔버스에서 undo 기능을 실행시키는 버튼입니다.
910
*/
10-
export const UndoButton = () => {
11+
export const UndoButton = memo(() => {
1112
const { workspace } = useWorkspaceStore();
1213

1314
const handleUndo = () => {
@@ -21,4 +22,4 @@ export const UndoButton = () => {
2122
<LeftArrow />
2223
</CircleButton>
2324
);
24-
};
25+
});

apps/client/src/entities/workspace/WorkspaceNameInput/WorkspaceNameInput.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FocusEventHandler, KeyboardEventHandler, useEffect, useState } from 'react';
1+
import { FocusEventHandler, KeyboardEventHandler, memo, useEffect, useState } from 'react';
22

33
import { Spinner } from '@/shared/ui';
44
import { useParams } from 'react-router-dom';
@@ -9,7 +9,7 @@ import { useWorkspaceStore } from '@/shared/store';
99
* @description
1010
* 워크스페이스 이름을 수정할 수 있는 컴포넌트입니다.
1111
*/
12-
export const WorkspaceNameInput = () => {
12+
export const WorkspaceNameInput = memo(() => {
1313
const { workspaceId } = useParams() as { workspaceId: string };
1414
const { mutate, isPending } = useUpdateWorkspaceName();
1515
const { name } = useWorkspaceStore();
@@ -68,4 +68,4 @@ export const WorkspaceNameInput = () => {
6868
</div>
6969
</>
7070
);
71-
};
71+
});

apps/client/src/pages/Workspacepage/WorkspacePage.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useGetWorkspace, usePreventLeaveWorkspacePage } from '@/shared/hooks';
44

55
import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
66
import { useParams } from 'react-router-dom';
7+
import { useShallow } from 'zustand/react/shallow';
78

89
/**
910
*
@@ -14,7 +15,13 @@ export const WorkspacePage = () => {
1415
const { workspaceId } = useParams<{ workspaceId: string }>();
1516
useGetWorkspace(workspaceId!);
1617
usePreventLeaveWorkspacePage();
17-
const { currentStep, isCoachMarkOpen, openCoachMark } = useCoachMarkStore();
18+
const { currentStep, isCoachMarkOpen, openCoachMark } = useCoachMarkStore(
19+
useShallow((state) => ({
20+
currentStep: state.currentStep,
21+
isCoachMarkOpen: state.isCoachMarkOpen,
22+
openCoachMark: state.openCoachMark,
23+
}))
24+
);
1825
const toolboxDiv = document.querySelector('.blocklyToolboxDiv');
1926

2027
useLayoutEffect(() => {
@@ -39,7 +46,7 @@ export const WorkspacePage = () => {
3946
<>
4047
<div className="flex h-screen flex-col">
4148
{isCoachMarkOpen && <CoachMark />}
42-
<WorkspacePageHeader />
49+
<WorkspacePageHeader currentStep={currentStep} />
4350
<WorkspaceContent />
4451
</div>
4552
<ImageTagModal />

apps/client/src/shared/ui/logo/Logo.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import BlackLogoText from '@/shared/assets/boolock_logo_black.svg?react';
22
import { Link } from 'react-router-dom';
33
import WhiteLogoText from '@/shared/assets/boolock_logo_white.svg?react';
4+
import { memo } from 'react';
45

56
type LogoProps = {
67
isBlack: boolean;
@@ -11,7 +12,7 @@ type LogoProps = {
1112
* @description
1213
* 로고 컴포넌트 (흰색/검은색), 클릭 시 홈페이지로 이동
1314
*/
14-
export const Logo = ({ isBlack }: LogoProps) => {
15+
export const Logo = memo(({ isBlack }: LogoProps) => {
1516
return (
1617
<Link to="/">
1718
<div className="flex items-center gap-3">
@@ -25,4 +26,4 @@ export const Logo = ({ isBlack }: LogoProps) => {
2526
</div>
2627
</Link>
2728
);
28-
};
29+
});
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { CodeExportButton, RedoButton, SaveButton, UndoButton } from '@/entities';
2+
import { memo } from 'react';
23

3-
import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
4+
type TWorkspaceHeaderButtons = {
5+
currentStep: number;
6+
};
7+
8+
export const WorkspaceHeaderButtons = memo(({ currentStep }: TWorkspaceHeaderButtons) => {
9+
const buttonsClassName = `flex items-center gap-3 ${currentStep === 4 ? 'z-[99999]' : ''}`;
410

5-
export const WorkspaceHeaderButtons = () => {
6-
const { currentStep } = useCoachMarkStore();
711
return (
8-
<div className={`flex items-center gap-3 ${currentStep === 4 ? 'z-[99999]' : ''}`}>
12+
<div className={buttonsClassName}>
913
<CodeExportButton />
1014
<SaveButton />
1115
<UndoButton />
1216
<RedoButton />
1317
</div>
1418
);
15-
};
19+
});
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
1-
import { WorkspaceNameInput } from '@/entities';
1+
import { HelpButton, WorkspaceNameInput } from '@/entities';
22
import { Logo } from '@/shared/ui';
33
import { WorkspaceHeaderButtons } from '../WorkspaceHeaderButtons/WorkspaceHeaderButtons';
4-
import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
5-
import Question from '@/shared/assets/question.svg?react';
4+
import { memo } from 'react';
65

76
/**
87
*
98
* @description
109
* 워크스페이스 페이지 헤더 컴포넌트
1110
*/
12-
export const WorkspacePageHeader = () => {
13-
const { openCoachMark } = useCoachMarkStore();
1411

15-
return (
16-
<div className="flex h-14 w-full flex-shrink-0 items-center justify-between border-b border-gray-100 bg-white pl-8 pr-4">
17-
<div className="flex items-center gap-5">
18-
<Logo isBlack={false} />
19-
<WorkspaceNameInput />
20-
</div>
21-
<div className="flex gap-11">
22-
<button
23-
className="text-medium-rg hover flex items-center gap-1 text-gray-300 hover:text-gray-500"
24-
onClick={openCoachMark}
25-
aria-label="도움말 버튼"
26-
>
27-
도움말 <Question />
28-
</button>
29-
<WorkspaceHeaderButtons />
30-
</div>
31-
</div>
32-
);
12+
type TWorkspacePageHeader = {
13+
currentStep: number;
3314
};
15+
16+
export const WorkspacePageHeader = memo(
17+
({ currentStep }: TWorkspacePageHeader) => {
18+
return (
19+
<div className="flex h-14 w-full flex-shrink-0 items-center justify-between border-b border-gray-100 bg-white pl-8 pr-4">
20+
<div className="flex items-center gap-5">
21+
<Logo isBlack={false} />
22+
<WorkspaceNameInput />
23+
</div>
24+
<div className="flex gap-11">
25+
<HelpButton />
26+
<WorkspaceHeaderButtons currentStep={currentStep} />
27+
</div>
28+
</div>
29+
);
30+
},
31+
(prevProps, nextProps) => {
32+
const prevIsFour = prevProps.currentStep !== 4;
33+
const nextIsFour = nextProps.currentStep !== 4;
34+
return prevIsFour && nextIsFour;
35+
}
36+
);

0 commit comments

Comments
 (0)