-
Notifications
You must be signed in to change notification settings - Fork 3
feat:매장 운영 업무 스케줄 위젯 추가(#28) #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| // 화면에 배치된 위젯 = layout. 추가는 카탈로그 항목(LayoutItem)을 넣고, 삭제는 layout에서 뺀다. | ||
| // 초기 레이아웃은 서버(RSC)에서 조회해 initialLayout으로 주입받는다(마운트 후 재조회 없음). | ||
| // 저장(쓰기)만 서버액션으로 위임한다. 영속화 키는 (workspaceId, pageType). editMode는 저장하지 않는다. | ||
| import { useCallback, useState } from 'react'; | ||
| import { useCallback, useEffect, useRef, useState } from 'react'; | ||
| import type { Layout, LayoutItem } from 'react-grid-layout'; | ||
|
|
||
| import { saveDashboardLayout, type DashboardLayoutState } from '@/entities/dashboard-layout'; | ||
|
|
@@ -26,43 +26,41 @@ export function useDashboardLayout({ | |
| }: UseDashboardLayoutParams) { | ||
| const [layout, setLayout] = useState<Layout>(initialLayout.layout); | ||
| const [editMode, setEditMode] = useState(false); | ||
| const didMountRef = useRef(false); | ||
|
|
||
| // TODO: DB 연동 — 변경 저장 (드래그 중 잦은 호출은 debounce 예정) | ||
| const commit = useCallback( | ||
| (next: Layout) => { | ||
| void saveDashboardLayout(workspaceId, pageType, { layout: next }); | ||
| }, | ||
| [workspaceId, pageType], | ||
| ); | ||
| useEffect(() => { | ||
| if (!didMountRef.current) { | ||
| didMountRef.current = true; | ||
| return; | ||
| } | ||
|
|
||
| void saveDashboardLayout(workspaceId, pageType, { layout }); | ||
| }, [layout, workspaceId, pageType]); | ||
|
Comment on lines
+29
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: The issue of onLayoutChange firing without a tangible position change is a known behavioral characteristic in react-grid-layout, often stemming from how the component handles internal state updates, mount events, or interaction events [1][2]. While version 2 (v2) includes significant architectural improvements, such as immutable layout updates, to address previous mutation-related bugs [3][4], users may still encounter unexpected triggers due to the following common patterns: 1. Lifecycle and Initialization: The onLayoutChange callback can fire during the component's initial mount or when the layout prop is first initialized, as the grid computes its internal layout state [1]. 2. Interaction Events: Features like droppable regions (dragenter/dragleave events) can trigger onLayoutChange even if no item is dropped, as the library may recalculate layout state in response to external drag interactions [2]. 3. Prop and Data Handling: Applying certain props (like data-grid) or managing layout state in external stores (e.g., Redux) without careful memoization can lead to redundant triggers or double-firing of callbacks [5][6]. 4. External Factors: In some environments, layout recalculations may occur due to external events like page scrolling or parent container resizing if the grid's dimensions are bound to these factors [7]. To mitigate these issues: - Implement a comparison check: Inside your onLayoutChange handler, verify if the new layout actually differs from your current state before triggering side effects (e.g., API calls or state updates) [1][6]. A deep comparison or a stringified check can be effective [6]. - Use specific event handlers: For drag and resize operations, prioritize using specific callbacks like onDragStop or onResizeStop to update your state, rather than relying solely on onLayoutChange if it is firing too frequently for your use case [2]. - Upgrade to v2: Ensure you are using the latest version (v2.2.0+), as it features a complete rewrite that utilizes immutable layout updates, significantly improving the accuracy of reference comparisons and reducing bugs related to layout mutation [3][4]. Citations:
초기 저장은 실제 레이아웃 변경 시에만 수행하도록 좁혀주세요. 🤖 Prompt for AI Agents |
||
|
|
||
| const handleLayoutChange = useCallback( | ||
| (next: Layout) => { | ||
| const positions = next.map(toPosition); | ||
| setLayout(positions); | ||
| commit(positions); | ||
| }, | ||
| [commit], | ||
| [], | ||
| ); | ||
|
|
||
| const addWidget = useCallback( | ||
| (item: LayoutItem) => | ||
| setLayout((prev) => { | ||
| if (prev.some((entry) => entry.i === item.i)) return prev; | ||
| const next = [...prev, toPosition(item)]; | ||
| commit(next); | ||
| return next; | ||
| return [...prev, toPosition(item)]; | ||
| }), | ||
| [commit], | ||
| [], | ||
| ); | ||
|
|
||
| const removeWidget = useCallback( | ||
| (id: string) => | ||
| setLayout((prev) => { | ||
| const next = prev.filter((entry) => entry.i !== id); | ||
| commit(next); | ||
| return next; | ||
| return prev.filter((entry) => entry.i !== id); | ||
| }), | ||
| [commit], | ||
| [], | ||
| ); | ||
|
|
||
| const toggleEdit = useCallback(() => setEditMode((prev) => !prev), []); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| // dashboard-work-schedule 위젯의 Public API | ||
| export { default as WorkScheduleSummary } from './ui/WorkScheduleSummary'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
TODO: 저장 debounce. 드래그/리사이즈 중
onLayoutChange가 매우 자주 발생하므로, DB 연동 시 각 이벤트마다 서버 액션이 호출됩니다. debounce(또는 드래그 종료 시점 저장) 도입이 필요합니다.원하시면 debounce 적용 구현을 작성하거나 추적용 이슈를 생성해 드릴까요?
🤖 Prompt for AI Agents