Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ dev,@types/pako,MIT,Copyright Microsoft Corporation
dev,@types/react,MIT,Copyright Microsoft Corporation
dev,@types/react-dom,MIT,Copyright Microsoft Corporation
dev,@wxt-dev/module-react,MIT,Copyright (c) 2023 Aaron
dev,@types/react-window,MIT,Copyright Microsoft Corporation
dev,@vitejs/plugin-react,MIT,Copyright (c) 2019-present Evan You & Vite Contributors
dev,ajv,MIT,Copyright 2015-2017 Evgeny Poberezkin
dev,browserstack-local,MIT,Copyright 2016 BrowserStack
Expand Down
3 changes: 1 addition & 2 deletions test/apps/react-heavy-spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.30.2",
"react-window": "1.8.11",
"react-window": "2.2.3",
"recharts": "2.15.4"
},
"devDependencies": {
"@types/node": "24.10.4",
"@types/react": "18.3.27",
"@types/react-dom": "18.3.7",
"@types/react-window": "1.8.8",
"@vitejs/plugin-react": "5.1.2",
"globals": "16.5.0",
"typescript": "5.9.3",
Expand Down
120 changes: 65 additions & 55 deletions test/apps/react-heavy-spa/src/components/Infrastructure/HostList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useMemo, useCallback, memo, Dispatch, SetStateAction } from 'react'
import { FixedSizeList as List } from 'react-window'
import { List, RowComponentProps } from 'react-window'
import { Host, HostStatus } from '../../types/data'
import './HostList.css'

Expand Down Expand Up @@ -55,43 +55,62 @@ const SortIcon = memo(
}
)

// Memoized row component for better performance
const HostRow = memo(
Comment on lines -58 to -59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: This memo wasn't effective in the first place, because onSelect was not wrapped in useCallback. I considered fixing this and keeping the memo, but then:

  • This component is very lightweight, so it doesn't matter
  • The app is called "react-heavy-spa", soo.... maybe we don't need to optimize it too much

({ host, isSelected, onSelect }: { host: Host; isSelected: boolean; onSelect: (host: Host) => void }) => (
<tr className={`host-row ${isSelected ? 'selected' : ''}`} onClick={() => onSelect(host)}>
<td className="host-name">{host.name}</td>
<td>
<span className={`status-badge ${host.status}`}>{host.status}</span>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill cpu" style={{ width: `${host.cpu}%` }} />
</div>
<span className="metric-value">{host.cpu}%</span>
</div>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill memory" style={{ width: `${host.memory}%` }} />
</div>
<span className="metric-value">{host.memory}%</span>
</div>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill disk" style={{ width: `${host.disk}%` }} />
</div>
<span className="metric-value">{host.disk}%</span>
</div>
</td>
<td className="network-cell">{host.network} Mbps</td>
<td className="uptime-cell">{formatUptime(host.uptime)}</td>
</tr>
type HostRowExtraProps = {
filteredAndSortedHosts: Host[]
selectedHost: Host | null
onHostSelect: (host: Host) => void
}

function HostRow({
filteredAndSortedHosts,
index,
style,
selectedHost,
onHostSelect,
}: RowComponentProps<HostRowExtraProps>) {
const host = filteredAndSortedHosts[index]
const isSelected = selectedHost?.id === host.id
return (
<div style={style}>
<table className="host-table" style={{ tableLayout: 'fixed', width: '100%' }}>
<tbody>
<tr className={`host-row ${isSelected ? 'selected' : ''}`} onClick={() => onHostSelect(host)}>
<td className="host-name">{host.name}</td>
<td>
<span className={`status-badge ${host.status}`}>{host.status}</span>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill cpu" style={{ width: `${host.cpu}%` }} />
</div>
<span className="metric-value">{host.cpu}%</span>
</div>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill memory" style={{ width: `${host.memory}%` }} />
</div>
<span className="metric-value">{host.memory}%</span>
</div>
</td>
<td>
<div className="metric-cell">
<div className="metric-bar">
<div className="metric-fill disk" style={{ width: `${host.disk}%` }} />
</div>
<span className="metric-value">{host.disk}%</span>
</div>
</td>
<td className="network-cell">{host.network} Mbps</td>
<td className="uptime-cell">{formatUptime(host.uptime)}</td>
</tr>
</tbody>
</table>
</div>
)
)
}

const HostList = memo(function HostList({
hosts,
Expand Down Expand Up @@ -248,26 +267,17 @@ const HostList = memo(function HostList({
</thead>
</table>
<div style={{ height: 'calc(100vh - 350px)', overflow: 'auto' }}>
<List
height={Math.min(filteredAndSortedHosts.length * 45 + 10, window.innerHeight - 350)}
itemCount={filteredAndSortedHosts.length}
itemSize={45}
width="100%"
<List<HostRowExtraProps>
rowCount={filteredAndSortedHosts.length}
rowComponent={HostRow}
rowHeight={45}
overscanCount={5}
>
{({ index, style }) => {
const host = filteredAndSortedHosts[index]
return (
<div style={style}>
<table className="host-table" style={{ tableLayout: 'fixed', width: '100%' }}>
<tbody>
<HostRow host={host} isSelected={selectedHost?.id === host.id} onSelect={onHostSelect} />
</tbody>
</table>
</div>
)
rowProps={{
filteredAndSortedHosts,
selectedHost,
onHostSelect,
}}
</List>
/>
</div>
</div>

Expand Down
39 changes: 12 additions & 27 deletions test/apps/react-heavy-spa/src/components/Logs/LogTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo, useCallback } from 'react'
import { FixedSizeList as List } from 'react-window'
import { List, RowComponentProps } from 'react-window'
import { LogEntry, LogLevel } from '../../types/data'
import './LogTable.css'

Expand All @@ -9,14 +9,10 @@ interface LogTableProps {
onLogSelect: (log: LogEntry) => void
}

interface LogRowProps {
index: number
style: React.CSSProperties
data?: {
logs: LogEntry[]
selectedLog: LogEntry | null
onLogSelect: (log: LogEntry) => void
}
interface LogRowExtraProps {
logs: LogEntry[]
selectedLog: LogEntry | null
onLogSelect: (log: LogEntry) => void
}

const LOG_LEVEL_COLORS: Record<LogLevel, string> = {
Expand Down Expand Up @@ -64,13 +60,9 @@ function truncateMessage(message: string, maxLength: number = 120): string {
return message.substring(0, maxLength) + '...'
}

function LogRow({ index, style, data }: LogRowProps) {
if (!data) return null

const { logs, selectedLog, onLogSelect } = data
function LogRow({ index, style, logs, selectedLog, onLogSelect }: RowComponentProps<LogRowExtraProps>) {
const log = logs[index]

if (!log) return null
const isSelected = selectedLog?.id === log.id

const handleClick = useCallback(() => {
Expand Down Expand Up @@ -182,20 +174,13 @@ export default function LogTable({ logs, selectedLog, onLogSelect }: LogTablePro
</div>

<div className="log-table-body">
<List<{
logs: LogEntry[]
selectedLog: LogEntry | null
onLogSelect: (log: LogEntry) => void
}>
height={600}
width="100%"
itemCount={logs.length}
itemSize={80}
itemData={itemData}
<List<LogRowExtraProps>
rowCount={logs.length}
rowHeight={80}
rowProps={itemData}
rowComponent={LogRow}
overscanCount={5}
>
{LogRow}
</List>
/>
</div>

<div className="log-table-footer">
Expand Down
48 changes: 0 additions & 48 deletions test/apps/react-heavy-spa/src/react-window.d.ts

This file was deleted.

45 changes: 8 additions & 37 deletions test/apps/react-heavy-spa/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7":
"@babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7":
version: 7.28.4
resolution: "@babel/runtime@npm:7.28.4"
checksum: 10c0/792ce7af9750fb9b93879cc9d1db175701c4689da890e6ced242ea0207c9da411ccf16dc04e689cc01158b28d7898c40d75598f4559109f761c12ce01e959bf7
Expand Down Expand Up @@ -778,24 +778,6 @@ __metadata:
languageName: node
linkType: hard

"@types/react-window@npm:1.8.8":
version: 1.8.8
resolution: "@types/react-window@npm:1.8.8"
dependencies:
"@types/react": "npm:*"
checksum: 10c0/2170a3957752603e8b994840c5d31b72ddf94c427c0f42b0175b343cc54f50fe66161d8871e11786ec7a59906bd33861945579a3a8f745455a3744268ec1069f
languageName: node
linkType: hard

"@types/react@npm:*":
version: 19.2.7
resolution: "@types/react@npm:19.2.7"
dependencies:
csstype: "npm:^3.2.2"
checksum: 10c0/a7b75f1f9fcb34badd6f84098be5e35a0aeca614bc91f93d2698664c0b2ba5ad128422bd470ada598238cebe4f9e604a752aead7dc6f5a92261d0c7f9b27cfd1
languageName: node
linkType: hard

"@types/react@npm:18.3.27":
version: 18.3.27
resolution: "@types/react@npm:18.3.27"
Expand Down Expand Up @@ -1253,13 +1235,12 @@ __metadata:
"@types/node": "npm:24.10.4"
"@types/react": "npm:18.3.27"
"@types/react-dom": "npm:18.3.7"
"@types/react-window": "npm:1.8.8"
"@vitejs/plugin-react": "npm:5.1.2"
globals: "npm:16.5.0"
react: "npm:18.3.1"
react-dom: "npm:18.3.1"
react-router-dom: "npm:6.30.2"
react-window: "npm:1.8.11"
react-window: "npm:2.2.3"
recharts: "npm:2.15.4"
typescript: "npm:5.9.3"
vite: "npm:5.4.21"
Expand Down Expand Up @@ -1408,13 +1389,6 @@ __metadata:
languageName: node
linkType: hard

"memoize-one@npm:>=3.1.1 <6":
version: 5.2.1
resolution: "memoize-one@npm:5.2.1"
checksum: 10c0/fd22dbe9a978a2b4f30d6a491fc02fb90792432ad0dab840dc96c1734d2bd7c9cdeb6a26130ec60507eb43230559523615873168bcbe8fafab221c30b11d54c1
languageName: node
linkType: hard

"minimatch@npm:^10.1.1":
version: 10.1.1
resolution: "minimatch@npm:10.1.1"
Expand Down Expand Up @@ -1724,16 +1698,13 @@ __metadata:
languageName: node
linkType: hard

"react-window@npm:1.8.11":
version: 1.8.11
resolution: "react-window@npm:1.8.11"
dependencies:
"@babel/runtime": "npm:^7.0.0"
memoize-one: "npm:>=3.1.1 <6"
"react-window@npm:2.2.3":
version: 2.2.3
resolution: "react-window@npm:2.2.3"
peerDependencies:
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
checksum: 10c0/5ae8da1bc5c47d8f0a428b28a600256e2db511975573e52cb65a9b27ed1a0e5b9f7b3bee5a54fb0da93956d782c24010be434be451072f46ba5a89159d2b3944
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
checksum: 10c0/7e97381b269560038c4c76c4ae17121d87efb628fce840fe382e73ae0e9816d4b68384a402340b0a08f9e4a47d8bd7328ae8502fd26b0d9399339a20e1c550be
languageName: node
linkType: hard

Expand Down