diff --git a/src/renderer/components/item-card/item-card.tsx b/src/renderer/components/item-card/item-card.tsx index 51a47a7ab..3f88bd4d3 100644 --- a/src/renderer/components/item-card/item-card.tsx +++ b/src/renderer/components/item-card/item-card.tsx @@ -9,7 +9,11 @@ import styles from './item-card.module.css'; import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls'; import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items'; import { getTitlePath } from '/@/renderer/components/item-list/helpers/get-title-path'; -import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state'; +import { + ItemListStateActions, + useItemDraggingState, + useItemSelectionState, +} from '/@/renderer/components/item-list/helpers/item-list-state'; import { ItemControls } from '/@/renderer/components/item-list/types'; import { useDragDrop } from '/@/renderer/hooks/use-drag-drop'; import { AppRoute } from '/@/renderer/router/routes'; @@ -141,10 +145,11 @@ const CompactItemCard = ({ withControls, }: ItemCardDerivativeProps) => { const [showControls, setShowControls] = useState(false); - const isSelected = + const itemRowId = data && internalState && typeof data === 'object' && 'id' in data - ? internalState.isSelected(internalState.extractRowId(data) || '') - : false; + ? internalState.extractRowId(data) + : undefined; + const isSelected = useItemSelectionState(internalState, itemRowId || undefined); const handleClick = useDoubleClick({ onDoubleClick: (e: React.MouseEvent) => { @@ -345,10 +350,11 @@ const DefaultItemCard = ({ withControls, }: ItemCardDerivativeProps) => { const [showControls, setShowControls] = useState(false); - const isSelected = + const itemRowId = data && internalState && typeof data === 'object' && 'id' in data - ? internalState.isSelected(internalState.extractRowId(data) || '') - : false; + ? internalState.extractRowId(data) + : undefined; + const isSelected = useItemSelectionState(internalState, itemRowId || undefined); const handleClick = useDoubleClick({ onDoubleClick: (e: React.MouseEvent) => { @@ -549,10 +555,11 @@ const PosterItemCard = ({ withControls, }: ItemCardDerivativeProps) => { const [showControls, setShowControls] = useState(false); - const isSelected = + const itemRowId = data && internalState && typeof data === 'object' && 'id' in data - ? internalState.isSelected(internalState.extractRowId(data) || '') - : false; + ? internalState.extractRowId(data) + : undefined; + const isSelected = useItemSelectionState(internalState, itemRowId || undefined); const { isDragging: isDraggingLocal, ref } = useDragDrop({ drag: { @@ -597,7 +604,9 @@ const PosterItemCard = ({ isEnabled: !!enableDrag && !!data, }); - const isDragging = data && internalState ? internalState.isDragging(data.id) : isDraggingLocal; + const itemId = data && internalState ? data.id : undefined; + const isDraggingState = useItemDraggingState(internalState, itemId); + const isDragging = isDraggingState || isDraggingLocal; const handleClick = useDoubleClick({ onDoubleClick: (e: React.MouseEvent) => { diff --git a/src/renderer/components/item-list/expanded-list-item.tsx b/src/renderer/components/item-list/expanded-list-item.tsx index 1da5df94e..bb62bef09 100644 --- a/src/renderer/components/item-list/expanded-list-item.tsx +++ b/src/renderer/components/item-list/expanded-list-item.tsx @@ -5,6 +5,7 @@ import styles from './expanded-list-item.module.css'; import { ItemListStateActions, ItemListStateItem, + useItemListStateSubscription, } from '/@/renderer/components/item-list/helpers/item-list-state'; import { ExpandedAlbumListItem } from '/@/renderer/features/albums/components/expanded-album-list-item'; import { Spinner } from '/@/shared/components/spinner/spinner'; @@ -16,7 +17,9 @@ interface ExpandedListItemProps { } export const ExpandedListItem = ({ internalState, itemType }: ExpandedListItemProps) => { - const expandedItems = internalState.getExpanded(); + const expandedItems = useItemListStateSubscription(internalState, () => + internalState ? internalState.getExpandedItemsCached() : [], + ); const currentItem = expandedItems[0]; if (!currentItem) { diff --git a/src/renderer/components/item-list/helpers/item-list-state.ts b/src/renderer/components/item-list/helpers/item-list-state.ts index c1e08dde2..f9afc2214 100644 --- a/src/renderer/components/item-list/helpers/item-list-state.ts +++ b/src/renderer/components/item-list/helpers/item-list-state.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useReducer } from 'react'; +import { useCallback, useMemo, useRef, useSyncExternalStore } from 'react'; import { itemListSelectors } from '/@/renderer/components/item-list/helpers/item-list-reducer-utils'; import { LibraryItem } from '/@/shared/types/domain-types'; @@ -84,6 +84,7 @@ export interface ItemListStateActions { getDraggingIds: () => string[]; getExpanded: () => unknown[]; getExpandedIds: () => string[]; + getExpandedItemsCached: () => unknown[]; getSelected: () => unknown[]; getSelectedIds: () => string[]; getVersion: () => number; @@ -281,11 +282,122 @@ export const initialItemListState: ItemListState = { version: 0, }; +/** + * External store for item list state that doesn't cause React rerenders + * Components can subscribe to specific state slices using useSyncExternalStore + */ +class ItemListStateStore { + // Cache for derived values to prevent unnecessary rerenders + private expandedItemsCache: null | unknown[] = null; + private expandedItemsCacheVersion: number = -1; + private listeners = new Set<() => void>(); + private state: ItemListState = { ...initialItemListState }; + + dispatch(action: ItemListAction): void { + this.state = itemListReducer(this.state, action); + // Invalidate caches when state changes + this.expandedItemsCache = null; + // Notify all subscribers + this.listeners.forEach((listener) => listener()); + } + + getExpandedItems(): unknown[] { + // Return cached array if state version hasn't changed + if ( + this.expandedItemsCache !== null && + this.expandedItemsCacheVersion === this.state.version + ) { + return this.expandedItemsCache; + } + // Create new array and cache it + this.expandedItemsCache = Array.from(this.state.expandedItems.values()); + this.expandedItemsCacheVersion = this.state.version; + return this.expandedItemsCache; + } + + getState(): ItemListState { + return this.state; + } + + subscribe(listener: () => void): () => void { + this.listeners.add(listener); + return () => { + this.listeners.delete(listener); + }; + } +} + +/** + * Hook to subscribe to specific state changes in the item list state + * Use this in components that need to rerender when state changes + */ +export const useItemListStateSubscription = ( + internalState: ItemListStateActions | undefined, + selector: (state: ItemListState | null) => T, +): T => { + const store = internalState ? ((internalState as any).__store as ItemListStateStore) : null; + + return useSyncExternalStore( + store?.subscribe.bind(store) || (() => () => {}), // Return no-op unsubscribe if no store + () => selector(store?.getState() || null), + ); +}; + +/** + * Hook to subscribe to selection state for a specific item + * Use this in components that need to rerender when a specific item's selection changes + */ +export const useItemSelectionState = ( + internalState: ItemListStateActions | undefined, + rowId: string | undefined, +): boolean => { + return useItemListStateSubscription(internalState, (state) => + state && rowId ? state.selected.has(rowId) : false, + ); +}; + +/** + * Hook to subscribe to expansion state for a specific item + * Use this in components that need to rerender when a specific item's expansion changes + */ +export const useItemExpansionState = ( + internalState: ItemListStateActions | undefined, + rowId: string | undefined, +): boolean => { + return useItemListStateSubscription(internalState, (state) => + state && rowId ? state.expanded.has(rowId) : false, + ); +}; + +/** + * Hook to subscribe to dragging state for a specific item + * Use this in components that need to rerender when a specific item's dragging state changes + */ +export const useItemDraggingState = ( + internalState: ItemListStateActions | undefined, + rowId: string | undefined, +): boolean => { + return useItemListStateSubscription(internalState, (state) => + state && rowId ? state.dragging.has(rowId) : false, + ); +}; + export const useItemListState = ( getDataFn?: () => unknown[], extractRowId?: (item: unknown) => string | undefined, ): ItemListStateActions => { - const [state, dispatch] = useReducer(itemListReducer, initialItemListState); + // Create store instance (stable across rerenders) + const storeRef = useRef(null); + if (!storeRef.current) { + storeRef.current = new ItemListStateStore(); + } + const store = storeRef.current; + + // DON'T subscribe here - this prevents rerenders when state changes + // Components that need to react should use useItemListStateSubscription + + // Get current state (this doesn't cause rerenders, it's just reading from the store) + const getCurrentState = useCallback(() => store.getState(), [store]); const extractRowIdFn = useCallback( (item: unknown) => { @@ -303,138 +415,156 @@ export const useItemListState = ( const setExpanded = useCallback( (items: ItemListStateItemWithRequiredProperties[]) => { - dispatch({ + store.dispatch({ extractRowId: extractRowIdFn, payload: items, type: 'SET_EXPANDED', }); }, - [extractRowIdFn], + [store, extractRowIdFn], ); const setDragging = useCallback( (items: ItemListStateItemWithRequiredProperties[]) => { - dispatch({ + store.dispatch({ extractRowId: extractRowIdFn, payload: items, type: 'SET_DRAGGING', }); }, - [extractRowIdFn], + [store, extractRowIdFn], ); const setSelected = useCallback( (items: ItemListStateItemWithRequiredProperties[]) => { - dispatch({ + store.dispatch({ extractRowId: extractRowIdFn, payload: items, type: 'SET_SELECTED', }); }, - [extractRowIdFn], + [store, extractRowIdFn], ); const toggleExpanded = useCallback( (item: ItemListStateItemWithRequiredProperties) => { - dispatch({ + store.dispatch({ extractRowId: extractRowIdFn, payload: item, type: 'TOGGLE_EXPANDED', }); }, - [extractRowIdFn], + [store, extractRowIdFn], ); const toggleSelected = useCallback( (item: ItemListStateItemWithRequiredProperties) => { - dispatch({ + store.dispatch({ extractRowId: extractRowIdFn, payload: item, type: 'TOGGLE_SELECTED', }); }, - [extractRowIdFn], + [store, extractRowIdFn], ); + // These methods read from the store without subscribing, so they don't cause rerenders const isExpanded = useCallback( (rowId: string) => { + const state = getCurrentState(); return itemListSelectors.isExpanded(state, rowId); }, - [state], + [getCurrentState], ); const isSelected = useCallback( (rowId: string) => { + const state = getCurrentState(); return itemListSelectors.isSelected(state, rowId); }, - [state], + [getCurrentState], ); const getExpanded = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.getExpanded(state); - }, [state]); + }, [getCurrentState]); + + const getExpandedItemsCached = useCallback(() => { + return store.getExpandedItems(); + }, [store]); const getDragging = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.getDragging(state); - }, [state]); + }, [getCurrentState]); const getSelected = useCallback(() => { + const state = getCurrentState(); const selectedItems = itemListSelectors.getSelected(state); const data = getDataFn ? getDataFn() : []; return sortByDataOrder(selectedItems, data, extractRowIdFn, false); - }, [state, getDataFn, extractRowIdFn]); + }, [getCurrentState, getDataFn, extractRowIdFn]); const getDraggingIds = useCallback(() => { + const state = getCurrentState(); return Array.from(state.dragging); - }, [state.dragging]); + }, [getCurrentState]); const getExpandedIds = useCallback(() => { + const state = getCurrentState(); return Array.from(state.expanded); - }, [state.expanded]); + }, [getCurrentState]); const getSelectedIds = useCallback(() => { + const state = getCurrentState(); const selectedIds = Array.from(state.selected); const data = getDataFn ? getDataFn() : []; return sortByDataOrder(selectedIds, data, extractRowIdFn, true); - }, [state.selected, getDataFn, extractRowIdFn]); + }, [getCurrentState, getDataFn, extractRowIdFn]); const clearExpanded = useCallback(() => { - dispatch({ type: 'CLEAR_EXPANDED' }); - }, []); + store.dispatch({ type: 'CLEAR_EXPANDED' }); + }, [store]); const clearDragging = useCallback(() => { - dispatch({ type: 'CLEAR_DRAGGING' }); - }, []); + store.dispatch({ type: 'CLEAR_DRAGGING' }); + }, [store]); const clearSelected = useCallback(() => { - dispatch({ type: 'CLEAR_SELECTED' }); - }, []); + store.dispatch({ type: 'CLEAR_SELECTED' }); + }, [store]); const clearAll = useCallback(() => { - dispatch({ type: 'CLEAR_ALL' }); - }, []); + store.dispatch({ type: 'CLEAR_ALL' }); + }, [store]); const getVersion = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.getVersion(state); - }, [state]); + }, [getCurrentState]); const hasExpanded = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.hasAnyExpanded(state); - }, [state]); + }, [getCurrentState]); const hasDragging = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.hasAnyDragging(state); - }, [state]); + }, [getCurrentState]); const hasSelected = useCallback(() => { + const state = getCurrentState(); return itemListSelectors.hasAnySelected(state); - }, [state]); + }, [getCurrentState]); const isDragging = useCallback( (rowId: string) => { + const state = getCurrentState(); return itemListSelectors.isDragging(state, rowId); }, - [state], + [getCurrentState], ); const getData = useCallback(() => { @@ -457,8 +587,12 @@ export const useItemListState = ( [getDataFn, extractRowId], ); - return useMemo( - () => ({ + // Expose the store so components can subscribe if needed + // Store it in the actions object for access + const actions = useMemo(() => { + const actionsObj = { + __getState: getCurrentState, + __store: store, clearAll, clearDragging, clearExpanded, @@ -470,6 +604,7 @@ export const useItemListState = ( getDraggingIds, getExpanded, getExpandedIds, + getExpandedItemsCached, getSelected, getSelectedIds, getVersion, @@ -484,33 +619,41 @@ export const useItemListState = ( setSelected, toggleExpanded, toggleSelected, - }), - [ - clearAll, - clearDragging, - clearExpanded, - clearSelected, - extractRowIdFn, - findItemIndex, - getData, - getDragging, - getDraggingIds, - getExpanded, - getExpandedIds, - getSelected, - getSelectedIds, - getVersion, - hasDragging, - hasExpanded, - hasSelected, - isDragging, - isExpanded, - isSelected, - setDragging, - setExpanded, - setSelected, - toggleExpanded, - toggleSelected, - ], - ); + } as ItemListStateActions & { + __getState: () => ItemListState; + __store: ItemListStateStore; + }; + return actionsObj; + }, [ + clearAll, + clearDragging, + clearExpanded, + clearSelected, + extractRowIdFn, + findItemIndex, + getData, + getDragging, + getDraggingIds, + getExpanded, + getExpandedIds, + getExpandedItemsCached, + getSelected, + getSelectedIds, + getVersion, + hasDragging, + hasExpanded, + hasSelected, + isDragging, + isExpanded, + isSelected, + setDragging, + setExpanded, + setSelected, + toggleExpanded, + toggleSelected, + store, + getCurrentState, + ]); + + return actions; }; diff --git a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx index 1b66fd126..2b5f23381 100644 --- a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx +++ b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx @@ -18,7 +18,10 @@ import styles from './item-detail-list.module.css'; import { ItemDetail } from '/@/renderer/components/item-detail/item-detail'; import { ExpandedListItem } from '/@/renderer/components/item-list/expanded-list-item'; -import { useItemListState } from '/@/renderer/components/item-list/helpers/item-list-state'; +import { + useItemListState, + useItemListStateSubscription, +} from '/@/renderer/components/item-list/helpers/item-list-state'; import { useElementSize } from '/@/shared/hooks/use-element-size'; import { useMergedRef } from '/@/shared/hooks/use-merged-ref'; import { LibraryItem } from '/@/shared/types/domain-types'; @@ -118,15 +121,17 @@ export const ItemDetailList = ({ } }, [itemDetailRef, initialize]); - const hasExpanded = internalState.hasExpanded(); + const hasExpanded = useItemListStateSubscription(internalState, (state) => + state ? state.expanded.size > 0 : false, + ); const handleExpand = useCallback( (_e: MouseEvent, item: unknown, itemType: LibraryItem) => { if (item && typeof item === 'object' && 'id' in item && 'serverId' in item) { internalState.toggleExpanded({ + _itemType: itemType, _serverId: item.serverId as string, id: item.id as string, - itemType: itemType, }); } }, diff --git a/src/renderer/components/item-list/item-grid-list/item-grid-list.tsx b/src/renderer/components/item-list/item-grid-list/item-grid-list.tsx index 19c56d9a2..a662d649e 100644 --- a/src/renderer/components/item-list/item-grid-list/item-grid-list.tsx +++ b/src/renderer/components/item-list/item-grid-list/item-grid-list.tsx @@ -39,6 +39,7 @@ import { ItemListStateActions, ItemListStateItemWithRequiredProperties, useItemListState, + useItemListStateSubscription, } from '/@/renderer/components/item-list/helpers/item-list-state'; import { ItemControls, ItemListHandle } from '/@/renderer/components/item-list/types'; import { animationProps } from '/@/shared/components/animations/animation-props'; @@ -320,8 +321,6 @@ const BaseItemGridList = ({ }, }); - const hasExpanded = internalState.hasExpanded(); - const tableMetaRef = useRef )} - - {hasExpanded && ( - - - - )} - + ); }; @@ -754,3 +747,25 @@ const ListComponent = memo((props: ListChildComponentProps) => { export const ItemGridList = memo(BaseItemGridList); ItemGridList.displayName = 'ItemGridList'; + +const ExpandedContainer = ({ + internalState, + itemType, +}: { + internalState: ItemListStateActions; + itemType: LibraryItem; +}) => { + const hasExpanded = useItemListStateSubscription(internalState, (state) => + state ? state.expanded.size > 0 : false, + ); + + return ( + + {hasExpanded && ( + + + + )} + + ); +}; diff --git a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx index 9d0a28fe4..fb3baadbe 100644 --- a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx +++ b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx @@ -17,6 +17,10 @@ import styles from './item-table-list-column.module.css'; import i18n from '/@/i18n/i18n'; import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items'; +import { + useItemDraggingState, + useItemSelectionState, +} from '/@/renderer/components/item-list/helpers/item-list-state'; import { ActionsColumn } from '/@/renderer/components/item-list/item-table-list/columns/actions-column'; import { AlbumArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-artists-column'; import { AlbumColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-column'; @@ -294,10 +298,16 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => { isEnabled: shouldEnableDrag, }); - const isDragging = + const itemRowId = item && typeof item === 'object' && 'id' in item && props.internalState - ? props.internalState.isDragging((item as any).id) - : isDraggingLocal; + ? props.internalState.extractRowId(item) + : undefined; + const isDraggingState = useItemDraggingState( + props.internalState, + itemRowId || + (item && typeof item === 'object' && 'id' in item ? (item as any).id : undefined), + ); + const isDragging = props.internalState ? isDraggingState : isDraggingLocal; const controls = props.controls; @@ -452,10 +462,11 @@ export const TableColumnTextContainer = ( const isDataRow = props.enableHeader ? props.rowIndex > 0 : true; const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex; const item = isDataRow ? props.data[props.rowIndex] : null; - const isSelected = + const itemRowId = item && typeof item === 'object' && 'id' in item - ? props.internalState.isSelected(props.internalState.extractRowId(item) || '') - : false; + ? props.internalState.extractRowId(item) + : undefined; + const isSelected = useItemSelectionState(props.internalState, itemRowId || undefined); const isDragging = props.isDragging ?? false; const mergedRef = useMergedRef(containerRef, props.dragRef ?? null); @@ -664,10 +675,11 @@ export const TableColumnContainer = ( const isDataRow = props.enableHeader ? props.rowIndex > 0 : true; const dataIndex = props.enableHeader ? props.rowIndex - 1 : props.rowIndex; const item = isDataRow ? props.data[props.rowIndex] : null; - const isSelected = + const itemRowId = item && typeof item === 'object' && 'id' in item - ? props.internalState.isSelected(props.internalState.extractRowId(item) || '') - : false; + ? props.internalState.extractRowId(item) + : undefined; + const isSelected = useItemSelectionState(props.internalState, itemRowId || undefined); const isDragging = props.isDragging ?? false; const mergedRef = useMergedRef(containerRef, props.dragRef ?? null); diff --git a/src/renderer/components/item-list/item-table-list/item-table-list.tsx b/src/renderer/components/item-list/item-table-list/item-table-list.tsx index 6d7df8332..562dcff8a 100644 --- a/src/renderer/components/item-list/item-table-list/item-table-list.tsx +++ b/src/renderer/components/item-list/item-table-list/item-table-list.tsx @@ -29,6 +29,7 @@ import { ItemListStateActions, ItemListStateItemWithRequiredProperties, useItemListState, + useItemListStateSubscription, } from '/@/renderer/components/item-list/helpers/item-list-state'; import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns'; import { useStickyTableGroupRows } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-table-group-rows'; @@ -1583,8 +1584,6 @@ const BaseItemTableList = ({ const internalState = useItemListState(getDataFn, extractRowId); - const hasExpanded = internalState.hasExpanded(); - // Helper function to get ItemListStateItemWithRequiredProperties (rowId is separate, not part of item) const getStateItem = useCallback( (item: any): ItemListStateItemWithRequiredProperties | null => { @@ -2169,17 +2168,33 @@ const BaseItemTableList = ({ totalColumnCount={totalColumnCount} totalRowCount={totalRowCount} /> - - {hasExpanded && ( - - - - )} - + ); }; export const ItemTableList = memo(BaseItemTableList); +const ExpandedContainer = ({ + internalState, + itemType, +}: { + internalState: ItemListStateActions; + itemType: LibraryItem; +}) => { + const hasExpanded = useItemListStateSubscription(internalState, (state) => + state ? state.expanded.size > 0 : false, + ); + + return ( + + {hasExpanded && ( + + + + )} + + ); +}; + ItemTableList.displayName = 'ItemTableList'; diff --git a/src/renderer/components/simple-item-table/simple-item-table.tsx b/src/renderer/components/simple-item-table/simple-item-table.tsx index dce7bf46e..38bed00de 100644 --- a/src/renderer/components/simple-item-table/simple-item-table.tsx +++ b/src/renderer/components/simple-item-table/simple-item-table.tsx @@ -1,11 +1,15 @@ import clsx from 'clsx'; -import { useId, useMemo } from 'react'; +import { memo, useId, useMemo } from 'react'; import styles from './simple-item-table.module.css'; import { createExtractRowId } from '/@/renderer/components/item-list/helpers/extract-row-id'; import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls'; -import { useItemListState } from '/@/renderer/components/item-list/helpers/item-list-state'; +import { + ItemListStateActions, + useItemListState, + useItemSelectionState, +} from '/@/renderer/components/item-list/helpers/item-list-state'; import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns'; import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list'; import { ItemTableListColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column'; @@ -176,68 +180,115 @@ export const SimpleItemTable = ({ )} - {data.map((item, rowIndex) => { - const adjustedRowIndex = enableHeader ? rowIndex + 1 : rowIndex; - const isSelected = - item && typeof item === 'object' && 'id' in item - ? internalState.isSelected(internalState.extractRowId(item) || '') - : false; - - const isLastRow = rowIndex === data.length - 1; - - return ( - - {parsedColumns.map((column, columnIndex) => { - const isLastColumn = columnIndex === parsedColumns.length - 1; - - return ( - - - - ); - })} - - ); - })} + {data.map((item, rowIndex) => ( + + ))} ); }; + +interface SimpleItemTableRowProps { + adjustedRowIndex: number; + enableAlternateRowColors: boolean; + enableHeader: boolean; + enableHorizontalBorders: boolean; + enableRowHoverHighlight: boolean; + enableVerticalBorders: boolean; + internalState: ItemListStateActions; + isLastRow: boolean; + item: unknown; + parsedColumns: ReturnType; + rowIndex: number; + tableId: string; + tableItemProps: TableItemProps; +} + +const SimpleItemTableRow = memo( + ({ + adjustedRowIndex, + enableAlternateRowColors, + enableHeader, + enableHorizontalBorders, + enableRowHoverHighlight, + enableVerticalBorders, + internalState, + isLastRow, + item, + parsedColumns, + rowIndex, + tableId, + tableItemProps, + }: SimpleItemTableRowProps) => { + const itemRowId = + item && typeof item === 'object' && 'id' in item + ? internalState.extractRowId(item) + : undefined; + const isSelected = useItemSelectionState(internalState, itemRowId || undefined); + + return ( + + {parsedColumns.map((column, columnIndex) => { + const isLastColumn = columnIndex === parsedColumns.length - 1; + + return ( + + + + ); + })} + + ); + }, +); + +SimpleItemTableRow.displayName = 'SimpleItemTableRow';