diff --git a/.cursor/rules/benchmarking.mdc b/.cursor/rules/benchmarking.mdc index 1f69b835ac6e..45ae39d52c99 100644 --- a/.cursor/rules/benchmarking.mdc +++ b/.cursor/rules/benchmarking.mdc @@ -31,7 +31,7 @@ Use this mapping when deciding which React benchmark scenarios are relevant to a - Relevant for: `@data-client/react` hooks, `@data-client/core` store initialization - All libraries -- **Update propagation** (`update-single-entity`, `update-shared-user-500-mounted`, `update-shared-user-10000-mounted`) +- **Update propagation** (`update-entity`, `update-user`, `update-user-10000`) - Exercises: store update → React rerender → DOM mutation - Relevant for: `@data-client/core` dispatch/reducer, `@data-client/react` subscription/selector - All libraries (normalization advantage shows with shared user at scale) @@ -41,7 +41,7 @@ Use this mapping when deciding which React benchmark scenarios are relevant to a - Relevant for: `@data-client/normalizr` denormalize memoization, Entity identity - All libraries (data-client should show fewest changed refs) -- **Sorted/derived view** (`sorted-view-mount-500`, `sorted-view-update-entity`) +- **Sorted/derived view** (`getlist-500-sorted`, `update-entity-sorted`) - Exercises: `Query` schema memoization via `useQuery` (data-client) vs `useMemo` sort (competitors) - Relevant for: `@data-client/endpoint` Query, `@data-client/normalizr` MemoCache, `@data-client/react` useQuery - All libraries @@ -58,8 +58,8 @@ Use this mapping when deciding which React benchmark scenarios are relevant to a | Category | Scenarios | Typical run-to-run spread | |---|---|---| -| **Stable** | `getlist-*`, `update-single-entity`, `ref-stability-*`, `sorted-view-mount-*` | 2–5% | -| **Moderate** | `update-shared-user-*`, `sorted-view-update-*` | 5–10% | +| **Stable** | `getlist-*`, `update-entity`, `ref-stability-*` | 2–5% | +| **Moderate** | `update-user-*`, `update-entity-sorted` | 5–10% | | **Volatile** | `memory-mount-unmount-cycle`, `startup-*`, `(react commit)` suffixes | 10–25% | Regressions >5% on stable scenarios or >15% on volatile scenarios are worth investigating. diff --git a/examples/benchmark-react/.babelrc.js b/examples/benchmark-react/.babelrc.js index bfd95e4230c7..8b171cb134bd 100644 --- a/examples/benchmark-react/.babelrc.js +++ b/examples/benchmark-react/.babelrc.js @@ -1,5 +1,5 @@ const options = { polyfillMethod: false }; -if (process.env.REACT_COMPILER === 'true') { +if (process.env.REACT_COMPILER !== 'false') { options.reactCompiler = {}; } diff --git a/examples/benchmark-react/README.md b/examples/benchmark-react/README.md index 432135ab0516..3c0b30631f4c 100644 --- a/examples/benchmark-react/README.md +++ b/examples/benchmark-react/README.md @@ -28,11 +28,11 @@ The repo has two benchmark suites: **Hot path (CI)** - **Get list** (`getlist-100`, `getlist-500`) — Time to show a ListView component that auto-fetches 100 or 500 issues from the list endpoint, then renders (unit: ms). Exercises the full fetch + normalization + render pipeline. -- **Update single entity** (`update-single-entity`) — Time to update one issue and propagate to the UI (unit: ms). -- **Update shared user (scaling)** (`update-shared-user-500-mounted`, `update-shared-user-10000-mounted`) — Update one shared user with 500 or 10,000 mounted issues to test subscriber scaling. Normalized cache: one store update, all views of that user update. +- **Get list sorted** (`getlist-500-sorted`) — Mount 500 issues through a sorted/derived view. data-client uses `useQuery(sortedIssuesQuery)` with `Query` schema memoization; competitors use `useMemo` + sort. +- **Update entity** (`update-entity`) — Time to update one issue and propagate to the UI (unit: ms). +- **Update entity sorted** (`update-entity-sorted`) — After mounting a sorted view, update one entity. data-client's `Query` memoization avoids re-sorting when sort keys are unchanged. +- **Update user (scaling)** (`update-user`, `update-user-10000`) — Update one shared user with 1,000 or 10,000 mounted issues to test subscriber scaling. Normalized cache: one store update, all views of that user update. - **Ref-stability** (`ref-stability-issue-changed`, `ref-stability-user-changed`) — Count of components that received a **new** object reference after an update (unit: count; smaller is better). Normalization keeps referential equality for unchanged entities. -- **Sorted view mount** (`sorted-view-mount-500`) — Mount 500 issues through a sorted/derived view. data-client uses `useQuery(sortedIssuesQuery)` with `Query` schema memoization; competitors use `useMemo` + sort. -- **Sorted view update** (`sorted-view-update-entity`) — After mounting a sorted view, update one entity. data-client's `Query` memoization avoids re-sorting when sort keys are unchanged. - **Invalidate and resolve** (`invalidate-and-resolve`) — data-client only; invalidates a cached endpoint and immediately re-resolves. Measures Suspense boundary round-trip. **With network (local comparison)** @@ -55,17 +55,17 @@ These are approximate values to help calibrate expectations. Exact numbers vary | Scenario | data-client | tanstack-query | swr | |---|---|---|---| | `getlist-100` | ~similar | ~similar | ~similar | -| `update-shared-user-500-mounted` | Low (one store write propagates) | Higher (list refetch) | Higher (list refetch) | +| `update-user` | Low (one store write propagates) | Higher (list refetch) | Higher (list refetch) | | `ref-stability-issue-changed` (100 mounted) | ~1 changed | ~100 changed (list refetch) | ~100 changed (list refetch) | | `ref-stability-user-changed` (100 mounted) | ~5 changed | ~100 changed (list refetch) | ~100 changed (list refetch) | -| `sorted-view-update-entity` | Fast (Query memoization skips re-sort) | Re-sorts on every issue change | Re-sorts on every issue change | +| `update-entity-sorted` | Fast (Query memoization skips re-sort) | Re-sorts on every issue change | Re-sorts on every issue change | ## Expected variance | Category | Scenarios | Typical run-to-run spread | |---|---|---| -| **Stable** | `getlist-*`, `update-single-entity`, `ref-stability-*`, `sorted-view-mount-*` | 2-5% | -| **Moderate** | `update-shared-user-*`, `sorted-view-update-*` | 5-10% | +| **Stable** | `getlist-*`, `update-entity`, `ref-stability-*` | 2-5% | +| **Moderate** | `update-user-*`, `update-entity-sorted` | 5-10% | | **Volatile** | `memory-mount-unmount-cycle`, `startup-*`, `(react commit)` suffixes | 10-25% | Regressions >5% on stable scenarios or >15% on volatile scenarios are worth investigating. @@ -107,24 +107,24 @@ Regressions >5% on stable scenarios or >15% on volatile scenarios are worth inve Or from repo root after a build: start preview in one terminal, then in another run `yarn workspace example-benchmark-react bench`. -3. **With React Compiler** +3. **Without React Compiler** - To measure the impact of React Compiler, build and bench with it enabled: + The default build includes React Compiler. To measure impact without it: ```bash cd examples/benchmark-react - yarn build:compiler # builds with babel-plugin-react-compiler + yarn build:no-compiler # builds without babel-plugin-react-compiler yarn preview & sleep 5 - yarn bench:compiler # labels results with [compiler] suffix + yarn bench:no-compiler # labels results with [no-compiler] suffix ``` - Or as a single command: `yarn bench:run:compiler`. + Or as a single command: `yarn bench:run:no-compiler`. - Results are labelled `[compiler]` so you can compare side-by-side with a normal run by loading both JSON files into the report viewer's history feature. + Results are labelled `[no-compiler]` so you can compare side-by-side with the default run by loading both JSON files into the report viewer's history feature. - You can also set the env vars directly for custom combinations: - - `REACT_COMPILER=true` — enables the Babel plugin at build time + Env vars for custom combinations: + - `REACT_COMPILER=false` — disables the Babel plugin at build time - `BENCH_LABEL=` — appends `[]` to all result names at bench time - `BENCH_PORT=` — port for `preview` server and bench runner (default `5173`) - `BENCH_BASE_URL=` — full base URL override (takes precedence over `BENCH_PORT`) @@ -163,8 +163,8 @@ Regressions >5% on stable scenarios or >15% on volatile scenarios are worth inve Scenarios are classified as `small` or `large` based on their cost: - - **Small** (3 warmup + 15 measurement): `getlist-100`, `update-single-entity`, `ref-stability-*`, `invalidate-and-resolve`, `unshift-item`, `delete-item` - - **Large** (1 warmup + 4 measurement): `getlist-500`, `update-shared-user-500-mounted`, `update-shared-user-10000-mounted`, `update-shared-user-with-network`, `sorted-view-mount-500`, `sorted-view-update-entity` + - **Small** (3 warmup + 15 measurement): `getlist-100`, `update-entity`, `ref-stability-*`, `invalidate-and-resolve`, `unshift-item`, `delete-item` + - **Large** (1 warmup + 4 measurement): `getlist-500`, `getlist-500-sorted`, `update-user`, `update-user-10000`, `update-entity-sorted`, `list-detail-switch` - **Memory** (opt-in, 1 warmup + 3 measurement): `memory-mount-unmount-cycle` — run with `--action memory` When running all scenarios (`yarn bench`), each group runs with its own warmup/measurement count. Use `--size` to run only one group. diff --git a/examples/benchmark-react/bench/runner.ts b/examples/benchmark-react/bench/runner.ts index 27d5e678ad61..74ab3d32f978 100644 --- a/examples/benchmark-react/bench/runner.ts +++ b/examples/benchmark-react/bench/runner.ts @@ -172,6 +172,13 @@ async function runScenario( ); } + if (scenario.renderLimit != null) { + await (bench as any).evaluate( + (api: any, n: number) => api.setRenderLimit(n), + scenario.renderLimit, + ); + } + const isMemory = scenario.action === 'mountUnmountCycle' && scenario.resultMetric === 'heapDelta'; diff --git a/examples/benchmark-react/bench/scenarios.ts b/examples/benchmark-react/bench/scenarios.ts index 54b4f91de266..8f96b9867e8d 100644 --- a/examples/benchmark-react/bench/scenarios.ts +++ b/examples/benchmark-react/bench/scenarios.ts @@ -56,6 +56,8 @@ interface BaseScenario { onlyLibs?: string[]; /** Result is deterministic (zero variance); run exactly once with no warmup. */ deterministic?: boolean; + /** Cap DOM rendering to first N items while keeping all data in the store. */ + renderLimit?: number; } const BASE_SCENARIOS: BaseScenario[] = [ @@ -73,10 +75,12 @@ const BASE_SCENARIOS: BaseScenario[] = [ size: 'large', }, { - nameSuffix: 'update-single-entity', + nameSuffix: 'update-entity', action: 'updateEntity', args: [1], category: 'hotPath', + mountCount: 1000, + renderLimit: 100, }, { nameSuffix: 'ref-stability-issue-changed', @@ -84,6 +88,8 @@ const BASE_SCENARIOS: BaseScenario[] = [ args: [1], resultMetric: 'issueRefChanged', category: 'hotPath', + mountCount: 1000, + renderLimit: 100, deterministic: true, }, { @@ -92,14 +98,17 @@ const BASE_SCENARIOS: BaseScenario[] = [ args: ['user0'], resultMetric: 'userRefChanged', category: 'hotPath', + mountCount: 1000, + renderLimit: 100, deterministic: true, }, { - nameSuffix: 'update-shared-user-500-mounted', + nameSuffix: 'update-user', action: 'updateUser', args: ['user0'], category: 'hotPath', - mountCount: 500, + mountCount: 1000, + renderLimit: 100, size: 'large', }, { @@ -111,41 +120,46 @@ const BASE_SCENARIOS: BaseScenario[] = [ size: 'large', }, { - nameSuffix: 'sorted-view-mount-500', + nameSuffix: 'getlist-500-sorted', action: 'mountSortedView', args: [500], category: 'hotPath', size: 'large', }, { - nameSuffix: 'sorted-view-update-entity', + nameSuffix: 'update-entity-sorted', action: 'updateEntity', args: [1], category: 'hotPath', - mountCount: 500, + mountCount: 1000, + renderLimit: 100, preMountAction: 'mountSortedView', size: 'large', }, { nameSuffix: 'list-detail-switch', action: 'listDetailSwitch', - args: [500], + args: [1000], category: 'hotPath', size: 'large', + renderLimit: 100, }, { - nameSuffix: 'update-shared-user-10000-mounted', + nameSuffix: 'update-user-10000', action: 'updateUser', args: ['user0'], category: 'hotPath', mountCount: 10000, size: 'large', + renderLimit: 100, }, { nameSuffix: 'invalidate-and-resolve', action: 'invalidateAndResolve', args: [1], category: 'hotPath', + mountCount: 1000, + renderLimit: 100, onlyLibs: ['data-client'], }, { @@ -153,21 +167,24 @@ const BASE_SCENARIOS: BaseScenario[] = [ action: 'unshiftItem', args: [], category: 'hotPath', - mountCount: 100, + mountCount: 1000, + renderLimit: 100, }, { nameSuffix: 'delete-item', action: 'deleteEntity', args: [1], category: 'hotPath', - mountCount: 100, + mountCount: 1000, + renderLimit: 100, }, { nameSuffix: 'move-item', action: 'moveItem', args: [1], category: 'hotPath', - mountCount: 100, + mountCount: 1000, + renderLimit: 100, preMountAction: 'initDoubleList', }, ]; @@ -188,6 +205,7 @@ export const SCENARIOS: Scenario[] = LIBRARIES.flatMap(lib => mountCount: base.mountCount, preMountAction: base.preMountAction, deterministic: base.deterministic, + renderLimit: base.renderLimit, }), ), ); diff --git a/examples/benchmark-react/bench/validate.ts b/examples/benchmark-react/bench/validate.ts index 708379dc49fc..f155befaf1e2 100644 --- a/examples/benchmark-react/bench/validate.ts +++ b/examples/benchmark-react/bench/validate.ts @@ -20,7 +20,6 @@ const BASE_URL = process.env.BENCH_BASE_URL ?? `http://localhost:${process.env.BENCH_PORT ?? '5173'}`; -// react-window virtualises; keep test counts within the visible window const TEST_ISSUE_COUNT = 20; // --------------------------------------------------------------------------- diff --git a/examples/benchmark-react/package.json b/examples/benchmark-react/package.json index 0a2c53aab40a..6e664820d728 100644 --- a/examples/benchmark-react/package.json +++ b/examples/benchmark-react/package.json @@ -5,15 +5,15 @@ "description": "React rendering benchmark comparing @data-client/react against other data libraries", "scripts": { "build": "BROWSERSLIST_ENV=2026 webpack --mode=production", - "build:compiler": "BROWSERSLIST_ENV=2026 REACT_COMPILER=true webpack --mode=production", + "build:no-compiler": "BROWSERSLIST_ENV=2026 REACT_COMPILER=false webpack --mode=production", "preview": "serve dist -l ${BENCH_PORT:-5173} --no-request-logging", "bench": "npx tsx bench/runner.ts", - "bench:compiler": "BENCH_LABEL=compiler npx tsx bench/runner.ts", + "bench:no-compiler": "BENCH_LABEL=no-compiler npx tsx bench/runner.ts", "bench:small": "npx tsx bench/runner.ts --size small", "bench:large": "npx tsx bench/runner.ts --size large", "bench:dc": "npx tsx bench/runner.ts --lib data-client", "bench:run": "yarn build && (yarn preview &) && sleep 5 && yarn bench", - "bench:run:compiler": "yarn build:compiler && (yarn preview &) && sleep 5 && yarn bench:compiler", + "bench:run:no-compiler": "yarn build:no-compiler && (yarn preview &) && sleep 5 && yarn bench:no-compiler", "validate": "npx tsx bench/validate.ts", "validate:run": "yarn build && (yarn preview &) && sleep 5 && yarn validate" }, @@ -28,7 +28,6 @@ "@tanstack/react-query": "5.62.7", "react": "19.2.3", "react-dom": "19.2.3", - "react-window": "^2.2.7", "swr": "2.4.1" }, "devDependencies": { diff --git a/examples/benchmark-react/src/data-client/index.tsx b/examples/benchmark-react/src/data-client/index.tsx index a33c0f261983..ed690ea4cde9 100644 --- a/examples/benchmark-react/src/data-client/index.tsx +++ b/examples/benchmark-react/src/data-client/index.tsx @@ -13,10 +13,7 @@ import { } from '@shared/benchHarness'; import { DOUBLE_LIST_STYLE, - ISSUE_HEIGHT, IssueRow, - IssuesRow, - LIST_STYLE, PlainIssueList, } from '@shared/components'; import { @@ -33,7 +30,6 @@ import { import { getIssue, patchIssue } from '@shared/server'; import type { Issue } from '@shared/types'; import React, { useCallback } from 'react'; -import { List } from 'react-window'; /** GCPolicy with no interval (won't fire during timing scenarios) and instant * expiry so an explicit sweep() collects all unreferenced data immediately. */ @@ -57,56 +53,50 @@ class BenchGCPolicy extends GCPolicy { const benchGC = new BenchGCPolicy(); /** Renders issues from the list endpoint (models rendering a list fetch response). */ -function ListView({ count }: { count: number }) { +function ListView({ count, limit }: { count: number; limit?: number }) { const { data: issues } = useDLE(IssueResource.getList, { count }); if (!issues) return null; const list = issues as Issue[]; setCurrentIssues(list); - return ( - - ); + return ; } /** Renders issues sorted by title via Query schema (memoized by MemoCache). */ -function SortedListView({ count }: { count: number }) { +function SortedListView({ count, limit }: { count: number; limit?: number }) { const { data: issues } = useDLE(sortedIssuesEndpoint, { count }); if (!issues?.length) return null; return (
- +
); } -function StateListView({ state, count }: { state: string; count: number }) { +function StateListView({ + state, + count, + limit, +}: { + state: string; + count: number; + limit?: number; +}) { const { data: issues } = useDLE(IssueResource.getList, { state, count }); if (!issues) return null; const list = issues as Issue[]; return (
{list.length} - +
); } -function DoubleListView({ count }: { count: number }) { +function DoubleListView({ count, limit }: { count: number; limit?: number }) { return (
- - + +
); } @@ -129,6 +119,7 @@ function BenchmarkHarness() { showDoubleList, doubleListCount, detailIssueNumber, + renderLimit, containerRef, measureUpdate, registerAPI, @@ -241,12 +232,14 @@ function BenchmarkHarness() { return (
- {listViewCount != null && } + {listViewCount != null && ( + + )} {showSortedView && sortedViewCount != null && ( - + )} {showDoubleList && doubleListCount != null && ( - + )} {detailIssueNumber != null && ( Loading...
}> diff --git a/examples/benchmark-react/src/shared/benchHarness.tsx b/examples/benchmark-react/src/shared/benchHarness.tsx index d1ea05ecccbe..fb3c561db976 100644 --- a/examples/benchmark-react/src/shared/benchHarness.tsx +++ b/examples/benchmark-react/src/shared/benchHarness.tsx @@ -80,6 +80,7 @@ export function useBenchState() { const [detailIssueNumber, setDetailIssueNumber] = useState( null, ); + const [renderLimit, setRenderLimit] = useState(); const containerRef = useRef(null); const completeResolveRef = useRef<(() => void) | null>(null); const apiRef = useRef(null as any); @@ -298,6 +299,7 @@ export function useBenchState() { setNetworkDelay, setMethodDelays, flushPendingMutations, + setRenderLimit, ...libraryActions, } as BenchAPI; }; @@ -321,6 +323,7 @@ export function useBenchState() { showDoubleList, doubleListCount, detailIssueNumber, + renderLimit, containerRef, measureMount, diff --git a/examples/benchmark-react/src/shared/components.tsx b/examples/benchmark-react/src/shared/components.tsx index aa5853a61e7c..92ecfd64fa42 100644 --- a/examples/benchmark-react/src/shared/components.tsx +++ b/examples/benchmark-react/src/shared/components.tsx @@ -1,11 +1,7 @@ import React from 'react'; -import type { RowComponentProps } from 'react-window'; import type { Issue, User } from './types'; -export const ISSUE_HEIGHT = 30; -export const VISIBLE_COUNT = 40; -export const LIST_STYLE = { height: ISSUE_HEIGHT * VISIBLE_COUNT } as const; export const DOUBLE_LIST_STYLE = { display: 'flex', gap: 8 } as const; function djb2(str: string): number { @@ -78,25 +74,19 @@ export function IssueRow({ issue }: { issue: Issue }) { ); } -/** Generic react-window row that renders an IssueRow from an issues array. */ -export function IssuesRow({ - index, - style, +/** Plain keyed list. React can reconcile inserts/deletes by key without + * re-rendering every row (unlike index-based virtualized lists). */ +export function PlainIssueList({ issues, -}: RowComponentProps<{ issues: Issue[] }>) { - return ( -
- -
- ); -} - -/** Plain (non-virtualized) list keyed by issue number. Renders up to VISIBLE_COUNT issues. */ -export function PlainIssueList({ issues }: { issues: Issue[] }) { + limit, +}: { + issues: Issue[]; + limit?: number; +}) { const visible = - issues.length > VISIBLE_COUNT ? issues.slice(0, VISIBLE_COUNT) : issues; + limit && issues.length > limit ? issues.slice(0, limit) : issues; return ( -
+
{visible.map(issue => ( ))} diff --git a/examples/benchmark-react/src/shared/types.ts b/examples/benchmark-react/src/shared/types.ts index 0eb02b094eea..8b566b1805cc 100644 --- a/examples/benchmark-react/src/shared/types.ts +++ b/examples/benchmark-react/src/shared/types.ts @@ -47,6 +47,8 @@ export interface BenchAPI { listDetailSwitch?(count: number): void; /** Trigger store garbage collection (data-client only). Used by memory scenarios to flush unreferenced data before heap measurement. */ triggerGC?(): void; + /** Cap DOM rendering to the first N items while keeping all data in the store. */ + setRenderLimit?(n: number | undefined): void; } declare global { @@ -142,4 +144,6 @@ export interface Scenario { preMountAction?: keyof BenchAPI; /** Result is deterministic (zero variance); run exactly once with no warmup. */ deterministic?: boolean; + /** Cap DOM rendering to first N items while keeping all data in the store. */ + renderLimit?: number; } diff --git a/examples/benchmark-react/src/swr/index.tsx b/examples/benchmark-react/src/swr/index.tsx index f17d81abce24..6d4a1fc635d0 100644 --- a/examples/benchmark-react/src/swr/index.tsx +++ b/examples/benchmark-react/src/swr/index.tsx @@ -5,10 +5,7 @@ import { } from '@shared/benchHarness'; import { DOUBLE_LIST_STYLE, - ISSUE_HEIGHT, IssueRow, - IssuesRow, - LIST_STYLE, PlainIssueList, } from '@shared/components'; import { @@ -21,7 +18,6 @@ import { setCurrentIssues } from '@shared/refStability'; import { UserResource, IssueResource } from '@shared/resources'; import type { Issue } from '@shared/types'; import React, { useCallback, useMemo } from 'react'; -import { List } from 'react-window'; import useSWR, { SWRConfig, useSWRConfig } from 'swr'; /** SWR fetcher: dispatches to shared resource fetch methods based on cache key */ @@ -42,19 +38,13 @@ const fetcher = (key: string): Promise => { return Promise.reject(new Error(`Unknown key: ${key}`)); }; -function SortedListView() { +function SortedListView({ limit }: { limit?: number }) { const { data: issues } = useSWR('issues:all', fetcher); const sorted = useMemo(() => (issues ? sortByTitle(issues) : []), [issues]); if (!sorted.length) return null; return (
- +
); } @@ -69,22 +59,22 @@ function DetailView({ number }: { number: number }) { ); } -function ListView({ count }: { count: number }) { +function ListView({ count, limit }: { count: number; limit?: number }) { const { data: issues } = useSWR(`issues:${count}`, fetcher); if (!issues) return null; setCurrentIssues(issues); - return ( - - ); + return ; } -function StateListView({ state, count }: { state: string; count: number }) { +function StateListView({ + state, + count, + limit, +}: { + state: string; + count: number; + limit?: number; +}) { const { data: issues } = useSWR( `issues:state:${state}:${count}`, fetcher, @@ -93,16 +83,16 @@ function StateListView({ state, count }: { state: string; count: number }) { return (
{issues.length} - +
); } -function DoubleListView({ count }: { count: number }) { +function DoubleListView({ count, limit }: { count: number; limit?: number }) { return (
- - + +
); } @@ -115,6 +105,7 @@ function BenchmarkHarness() { showDoubleList, doubleListCount, detailIssueNumber, + renderLimit, containerRef, measureUpdate, registerAPI, @@ -196,10 +187,12 @@ function BenchmarkHarness() { return (
- {listViewCount != null && } - {showSortedView && } + {listViewCount != null && ( + + )} + {showSortedView && } {showDoubleList && doubleListCount != null && ( - + )} {detailIssueNumber != null && }
diff --git a/examples/benchmark-react/src/tanstack-query/index.tsx b/examples/benchmark-react/src/tanstack-query/index.tsx index 900de1e398f3..9e038044f1f8 100644 --- a/examples/benchmark-react/src/tanstack-query/index.tsx +++ b/examples/benchmark-react/src/tanstack-query/index.tsx @@ -5,10 +5,7 @@ import { } from '@shared/benchHarness'; import { DOUBLE_LIST_STYLE, - ISSUE_HEIGHT, IssueRow, - IssuesRow, - LIST_STYLE, PlainIssueList, } from '@shared/components'; import { @@ -27,7 +24,6 @@ import { useQueryClient, } from '@tanstack/react-query'; import React, { useCallback, useMemo } from 'react'; -import { List } from 'react-window'; function queryFn({ queryKey }: { queryKey: readonly unknown[] }): Promise { const [type, id] = queryKey as [string, string | number | undefined]; @@ -51,7 +47,7 @@ const queryClient = new QueryClient({ }, }); -function SortedListView() { +function SortedListView({ limit }: { limit?: number }) { const { data: issues } = useQuery({ queryKey: ['issues', 'all'], queryFn, @@ -63,13 +59,7 @@ function SortedListView() { if (!sorted.length) return null; return (
- +
); } @@ -87,7 +77,7 @@ function DetailView({ number }: { number: number }) { ); } -function ListView({ count }: { count: number }) { +function ListView({ count, limit }: { count: number; limit?: number }) { const { data: issues } = useQuery({ queryKey: ['issues', count], queryFn, @@ -95,18 +85,18 @@ function ListView({ count }: { count: number }) { if (!issues) return null; const list = issues as Issue[]; setCurrentIssues(list); - return ( - - ); + return ; } -function StateListView({ state, count }: { state: string; count: number }) { +function StateListView({ + state, + count, + limit, +}: { + state: string; + count: number; + limit?: number; +}) { const { data: issues } = useQuery({ queryKey: ['issues', { state, count }], queryFn, @@ -116,16 +106,16 @@ function StateListView({ state, count }: { state: string; count: number }) { return (
{list.length} - +
); } -function DoubleListView({ count }: { count: number }) { +function DoubleListView({ count, limit }: { count: number; limit?: number }) { return (
- - + +
); } @@ -138,6 +128,7 @@ function BenchmarkHarness() { showDoubleList, doubleListCount, detailIssueNumber, + renderLimit, containerRef, measureUpdate, registerAPI, @@ -222,10 +213,12 @@ function BenchmarkHarness() { return (
- {listViewCount != null && } - {showSortedView && } + {listViewCount != null && ( + + )} + {showSortedView && } {showDoubleList && doubleListCount != null && ( - + )} {detailIssueNumber != null && }
diff --git a/yarn.lock b/yarn.lock index bb58c9533d5f..27650ce296d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14654,7 +14654,6 @@ __metadata: playwright: "npm:1.58.2" react: "npm:19.2.3" react-dom: "npm:19.2.3" - react-window: "npm:^2.2.7" serve: "npm:14.2.6" swr: "npm:2.4.1" tsx: "npm:4.21.0" @@ -25056,16 +25055,6 @@ __metadata: languageName: node linkType: hard -"react-window@npm:^2.2.7": - version: 2.2.7 - resolution: "react-window@npm:2.2.7" - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - checksum: 10c0/4eba3bce2083fa53ac674513078fb23d4dc3ad7dfd12ea5733863b583ea1472294df791947a2b58f27bab45138cedf7a63fdc19b0420823bf19749aa10455b81 - languageName: node - linkType: hard - "react@npm:19.2.3": version: 19.2.3 resolution: "react@npm:19.2.3"