fix(pop-dashboard): React Hooks 규칙 위반 수정 + ConfigPanel props 정합성 + 방어 코드 강화
- PopDashboardComponent: early return을 모든 hooks 이후로 이동 (Rules of Hooks) - PopDashboardConfigPanel: onChange -> onUpdate prop 이름 정합, 빈 객체 config 방어 - PopDashboardPreview: Array.isArray 방어 추가 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4f3e9ec19e
commit
73e3d56381
|
|
@ -111,19 +111,10 @@ export function PopDashboardComponent({
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [containerWidth, setContainerWidth] = useState(300);
|
||||
|
||||
// 빈 설정
|
||||
if (!config || !config.items.length) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center bg-muted/20">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
대시보드 아이템을 추가하세요
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 보이는 아이템만 필터링
|
||||
const visibleItems = config.items.filter((item) => item.visible);
|
||||
// 보이는 아이템만 필터링 (hooks 이전에 early return 불가하므로 빈 배열 허용)
|
||||
const visibleItems = Array.isArray(config?.items)
|
||||
? config.items.filter((item) => item.visible)
|
||||
: [];
|
||||
|
||||
// 컨테이너 크기 감지
|
||||
useEffect(() => {
|
||||
|
|
@ -140,6 +131,7 @@ export function PopDashboardComponent({
|
|||
}, []);
|
||||
|
||||
// 데이터 로딩 함수
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const fetchAllData = useCallback(async () => {
|
||||
if (!visibleItems.length) {
|
||||
setLoading(false);
|
||||
|
|
@ -165,7 +157,6 @@ export function PopDashboardComponent({
|
|||
|
||||
setDataMap(newDataMap);
|
||||
setLoading(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(visibleItems.map((i) => i.id))]);
|
||||
|
||||
// 초기 로딩 + 주기적 새로고침
|
||||
|
|
@ -186,6 +177,17 @@ export function PopDashboardComponent({
|
|||
};
|
||||
}, [fetchAllData, visibleItems]);
|
||||
|
||||
// 빈 설정 (모든 hooks 이후에 early return)
|
||||
if (!config || !config.items?.length) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center bg-muted/20">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
대시보드 아이템을 추가하세요
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 단일 아이템 렌더링
|
||||
const renderSingleItem = (item: DashboardItem) => {
|
||||
const itemData = dataMap[item.id];
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import { validateExpression } from "./utils/formula";
|
|||
|
||||
interface ConfigPanelProps {
|
||||
config: PopDashboardConfig | undefined;
|
||||
onChange: (config: PopDashboardConfig) => void;
|
||||
onUpdate: (config: PopDashboardConfig) => void;
|
||||
}
|
||||
|
||||
// ===== 기본값 =====
|
||||
|
|
@ -806,9 +806,10 @@ function GridLayoutEditor({
|
|||
|
||||
export function PopDashboardConfigPanel({
|
||||
config,
|
||||
onChange,
|
||||
onUpdate: onChange,
|
||||
}: ConfigPanelProps) {
|
||||
const cfg = config ?? DEFAULT_CONFIG;
|
||||
// config가 빈 객체 {}로 전달될 수 있으므로 spread로 기본값 보장
|
||||
const cfg: PopDashboardConfig = { ...DEFAULT_CONFIG, ...(config || {}) };
|
||||
const [activeTab, setActiveTab] = useState<"basic" | "items" | "layout">(
|
||||
"basic"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ export function PopDashboardPreviewComponent({
|
|||
}: {
|
||||
config?: PopDashboardConfig;
|
||||
}) {
|
||||
if (!config || !config.items.length) {
|
||||
// config가 빈 객체 {} 또는 items가 없는 경우 방어
|
||||
if (!config || !Array.isArray(config.items) || !config.items.length) {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-1 overflow-hidden">
|
||||
<BarChart3 className="h-6 w-6 text-muted-foreground/50" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue