fix: [공통] SearchableSelectionModal 테이블 HTML 유효성 에러 수정
- renderItem이 <tr>을 반환할 때 <div>로 래핑하여 발생하던 hydration 에러 해결 - cloneElement로 key/onClick을 직접 주입하여 <tbody><div><tr> 구조 방지 - 영향범위: OrderSelectModal, SalesOrderSelectModal, TaxInvoiceIssuance
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect, cloneElement, isValidElement } from 'react';
|
||||||
import { Search, X, Loader2 } from 'lucide-react';
|
import { Search, X, Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -156,11 +156,34 @@ export function SearchableSelectionModal<T>(props: SearchableSelectionModalProps
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemElements = items.map((item) => (
|
const itemElements = items.map((item) => {
|
||||||
<div key={keyExtractor(item)} onClick={() => handleItemClick(item)} className="cursor-pointer">
|
const key = keyExtractor(item);
|
||||||
{renderItem(item, isSelected(item))}
|
const rendered = renderItem(item, isSelected(item));
|
||||||
</div>
|
|
||||||
));
|
// renderItem이 유효한 React 엘리먼트를 반환하면 key와 onClick을 직접 주입 (div 래핑 없이)
|
||||||
|
// 이렇게 하면 <TableRow> 등 테이블 요소를 <div>로 감싸는 HTML 유효성 에러를 방지
|
||||||
|
if (isValidElement(rendered)) {
|
||||||
|
return cloneElement(rendered as React.ReactElement<Record<string, unknown>>, {
|
||||||
|
key,
|
||||||
|
onClick: (e: React.MouseEvent) => {
|
||||||
|
// 기존 onClick이 있으면 먼저 호출
|
||||||
|
const existingOnClick = (rendered.props as Record<string, unknown>)?.onClick;
|
||||||
|
if (typeof existingOnClick === 'function') {
|
||||||
|
(existingOnClick as (e: React.MouseEvent) => void)(e);
|
||||||
|
}
|
||||||
|
handleItemClick(item);
|
||||||
|
},
|
||||||
|
className: `cursor-pointer ${(rendered.props as Record<string, unknown>)?.className || ''}`.trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 일반 텍스트/fragment인 경우 기존 div 래핑 유지
|
||||||
|
return (
|
||||||
|
<div key={key} onClick={() => handleItemClick(item)} className="cursor-pointer">
|
||||||
|
{rendered}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if (listWrapper) {
|
if (listWrapper) {
|
||||||
const selectState = mode === 'multiple'
|
const selectState = mode === 'multiple'
|
||||||
|
|||||||
Reference in New Issue
Block a user