From 45ad99cb382aa57ce9f5ee4395fae14bbb71169a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 5 Mar 2026 21:59:44 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[=EA=B3=B5=ED=86=B5]=20SearchableSelecti?= =?UTF-8?q?onModal=20=ED=85=8C=EC=9D=B4=EB=B8=94=20HTML=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderItem이 을 반환할 때
로 래핑하여 발생하던 hydration 에러 해결 - cloneElement로 key/onClick을 직접 주입하여
구조 방지 - 영향범위: OrderSelectModal, SalesOrderSelectModal, TaxInvoiceIssuance --- .../SearchableSelectionModal.tsx | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/components/organisms/SearchableSelectionModal/SearchableSelectionModal.tsx b/src/components/organisms/SearchableSelectionModal/SearchableSelectionModal.tsx index 394625be..84ebddbb 100644 --- a/src/components/organisms/SearchableSelectionModal/SearchableSelectionModal.tsx +++ b/src/components/organisms/SearchableSelectionModal/SearchableSelectionModal.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useEffect, cloneElement, isValidElement } from 'react'; import { Search, X, Loader2 } from 'lucide-react'; import { @@ -156,11 +156,34 @@ export function SearchableSelectionModal(props: SearchableSelectionModalProps ); } - const itemElements = items.map((item) => ( -
handleItemClick(item)} className="cursor-pointer"> - {renderItem(item, isSelected(item))} -
- )); + const itemElements = items.map((item) => { + const key = keyExtractor(item); + const rendered = renderItem(item, isSelected(item)); + + // renderItem이 유효한 React 엘리먼트를 반환하면 key와 onClick을 직접 주입 (div 래핑 없이) + // 이렇게 하면 등 테이블 요소를
로 감싸는 HTML 유효성 에러를 방지 + if (isValidElement(rendered)) { + return cloneElement(rendered as React.ReactElement>, { + key, + onClick: (e: React.MouseEvent) => { + // 기존 onClick이 있으면 먼저 호출 + const existingOnClick = (rendered.props as Record)?.onClick; + if (typeof existingOnClick === 'function') { + (existingOnClick as (e: React.MouseEvent) => void)(e); + } + handleItemClick(item); + }, + className: `cursor-pointer ${(rendered.props as Record)?.className || ''}`.trim(), + }); + } + + // 일반 텍스트/fragment인 경우 기존 div 래핑 유지 + return ( +
handleItemClick(item)} className="cursor-pointer"> + {rendered} +
+ ); + }); if (listWrapper) { const selectState = mode === 'multiple'