Skip to content

Commit 46abc38

Browse files
committed
fix: performance issue when there are many items caused by ItemContextMenu
1 parent 6bdfaf9 commit 46abc38

File tree

2 files changed

+83
-67
lines changed

2 files changed

+83
-67
lines changed

src/components/databrowser/components/display/display-list.tsx

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ export const ListDisplay = ({ dataKey, type }: { dataKey: string; type: ListData
3939
<InfiniteScroll query={query}>
4040
<div className="pr-3">
4141
<table className="w-full">
42-
<tbody>
43-
<ListItems dataKey={dataKey} type={type} query={query} />
44-
</tbody>
42+
<ItemContextMenu dataKey={dataKey} type={type}>
43+
<tbody>
44+
<ListItems dataKey={dataKey} type={type} query={query} />
45+
</tbody>
46+
</ItemContextMenu>
4547
</table>
4648
</div>
4749
</InfiniteScroll>
@@ -50,7 +52,7 @@ export const ListDisplay = ({ dataKey, type }: { dataKey: string; type: ListData
5052
)
5153
}
5254

53-
type ItemData = {
55+
export type ItemData = {
5456
key: string
5557
value?: string
5658
}
@@ -75,63 +77,58 @@ export const ListItems = ({
7577
return (
7678
<>
7779
{keys.map(({ key, value }, i) => (
78-
<ItemContextMenu
80+
<tr
7981
key={`${dataKey}-${key}-${i}`}
80-
dataKey={dataKey}
81-
type={type}
82-
itemKey={key}
83-
itemValue={value}
82+
data-item-key={key}
83+
data-item-value={value}
84+
onClick={() => {
85+
setSelectedListItem({ key, value })
86+
}}
87+
className="h-10 border-b border-b-zinc-100 hover:bg-zinc-50"
8488
>
85-
<tr
86-
onClick={() => {
87-
setSelectedListItem({ key, value })
88-
}}
89-
className="h-10 border-b border-b-zinc-100 hover:bg-zinc-50"
89+
<td
90+
className={cn(
91+
"cursor-pointer truncate px-3",
92+
type === "list" || type === "stream" ? "w-32 min-w-24" : "max-w-0"
93+
)}
9094
>
95+
{key}
96+
</td>
97+
{value !== undefined && (
9198
<td
92-
className={cn(
93-
"cursor-pointer truncate px-3",
94-
type === "list" || type === "stream" ? "w-32 min-w-24" : "max-w-0"
95-
)}
99+
className={cn("cursor-pointer truncate px-3", type === "zset" ? "w-24" : "max-w-0")}
96100
>
97-
{key}
101+
{value}
98102
</td>
99-
{value !== undefined && (
100-
<td
101-
className={cn("cursor-pointer truncate px-3", type === "zset" ? "w-24" : "max-w-0")}
102-
>
103-
{value}
104-
</td>
105-
)}
106-
{type !== "stream" && (
107-
<td
108-
width={20}
109-
className="px-3"
110-
onClick={(e) => {
103+
)}
104+
{type !== "stream" && (
105+
<td
106+
width={20}
107+
className="px-3"
108+
onClick={(e) => {
109+
e.stopPropagation()
110+
}}
111+
>
112+
<DeleteAlertDialog
113+
deletionType="item"
114+
onDeleteConfirm={(e) => {
111115
e.stopPropagation()
116+
editItem({
117+
type,
118+
dataKey,
119+
itemKey: key,
120+
// For deletion
121+
newKey: undefined,
122+
})
112123
}}
113124
>
114-
<DeleteAlertDialog
115-
deletionType="item"
116-
onDeleteConfirm={(e) => {
117-
e.stopPropagation()
118-
editItem({
119-
type,
120-
dataKey,
121-
itemKey: key,
122-
// For deletion
123-
newKey: undefined,
124-
})
125-
}}
126-
>
127-
<Button size="icon-sm" variant="secondary" onClick={(e) => e.stopPropagation()}>
128-
<IconTrash className="size-4 text-zinc-500" />
129-
</Button>
130-
</DeleteAlertDialog>
131-
</td>
132-
)}
133-
</tr>
134-
</ItemContextMenu>
125+
<Button size="icon-sm" variant="secondary" onClick={(e) => e.stopPropagation()}>
126+
<IconTrash className="size-4 text-zinc-500" />
127+
</Button>
128+
</DeleteAlertDialog>
129+
</td>
130+
)}
131+
</tr>
135132
))}
136133
</>
137134
)

src/components/databrowser/components/item-context-menu.tsx

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,19 @@ import { toast } from "@/components/ui/use-toast"
1212

1313
import { useEditListItem } from "../hooks"
1414
import { DeleteAlertDialog } from "./display/delete-alert-dialog"
15+
import type { ItemData } from "./display/display-list"
1516

1617
export const ItemContextMenu = ({
1718
children,
1819
dataKey,
19-
itemKey,
20-
itemValue,
2120
type,
2221
}: PropsWithChildren<{
2322
dataKey: string
2423
type: ListDataType
25-
itemKey: string
26-
itemValue?: string
2724
}>) => {
2825
const { mutate: editItem } = useEditListItem()
2926
const [isAlertOpen, setAlertOpen] = useState(false)
27+
const [data, setData] = useState<ItemData | undefined>()
3028

3129
return (
3230
<>
@@ -36,33 +34,54 @@ export const ItemContextMenu = ({
3634
onOpenChange={setAlertOpen}
3735
onDeleteConfirm={(e) => {
3836
e.stopPropagation()
39-
editItem({
40-
type,
41-
dataKey,
42-
itemKey,
43-
// For deletion
44-
newKey: undefined,
45-
})
37+
if (data) {
38+
editItem({
39+
type,
40+
dataKey,
41+
itemKey: data?.key,
42+
// For deletion
43+
newKey: undefined,
44+
})
45+
}
4646
setAlertOpen(false)
4747
}}
4848
/>
4949
<ContextMenu>
50-
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
50+
<ContextMenuTrigger
51+
asChild
52+
// NOTE: We did not put the ContextMenu on every key because of performance reasons
53+
onContextMenu={(e) => {
54+
const el = e.target as HTMLElement
55+
const item = el.closest("[data-item-key]")
56+
57+
if (item && item instanceof HTMLElement && item.dataset.itemKey !== undefined) {
58+
setData({
59+
key: item.dataset.itemKey,
60+
value: item.dataset.itemValue,
61+
})
62+
} else {
63+
throw new Error("Key not found")
64+
}
65+
}}
66+
>
67+
{children}
68+
</ContextMenuTrigger>
5169
<ContextMenuContent>
5270
<ContextMenuItem
5371
onClick={() => {
54-
navigator.clipboard.writeText(itemKey)
72+
if (!data) return
73+
navigator.clipboard.writeText(data?.key)
5574
toast({
5675
description: "Key copied to clipboard",
5776
})
5877
}}
5978
>
6079
Copy key
6180
</ContextMenuItem>
62-
{itemValue !== undefined && (
81+
{data?.value && (
6382
<ContextMenuItem
6483
onClick={() => {
65-
navigator.clipboard.writeText(itemValue)
84+
navigator.clipboard.writeText(data?.value ?? "")
6685
toast({
6786
description: "Value copied to clipboard",
6887
})

0 commit comments

Comments
 (0)