-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
test: add react-router navigation speed benchmark #6795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| import { cleanup, render } from '@testing-library/react' | ||
| import { act } from 'react' | ||
| import { bench, describe } from 'vitest' | ||
| import { rootRouteId } from '@tanstack/router-core' | ||
| import { | ||
| Outlet, | ||
| RouterProvider, | ||
| createMemoryHistory, | ||
| createRootRoute, | ||
| createRoute, | ||
| createRouter, | ||
| useParams, | ||
| useSearch, | ||
| } from '../src' | ||
| import type { NavigateOptions } from '@tanstack/router-core' | ||
|
|
||
| function createTestRouter() { | ||
| function runPerfSelectorComputation(seed: number) { | ||
| let value = Math.trunc(seed) | 0 | ||
|
|
||
| for (let index = 0; index < 100; index++) { | ||
| value = (value * 1664525 + 1013904223 + index) >>> 0 | ||
| } | ||
|
|
||
| return value | ||
| } | ||
|
|
||
| const selectors = Array.from({ length: 20 }, (_, index) => index) | ||
|
|
||
| function Params() { | ||
| const params = useParams({ | ||
| from: rootRouteId, | ||
| select: (params) => runPerfSelectorComputation(Number(params.id ?? 0)), | ||
| }) | ||
| void params | ||
| return null | ||
| } | ||
|
|
||
| function Search() { | ||
| const search = useSearch({ | ||
| from: rootRouteId, | ||
| select: (search) => runPerfSelectorComputation(Number(search.id ?? 0)), | ||
| }) | ||
| void search | ||
| return null | ||
| } | ||
|
Comment on lines
+30
to
+46
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Explore the speed.bench.tsx file structure and context
# First, read the full benchmark file to understand context
echo "=== Full speed.bench.tsx file ==="
cat packages/react-router/tests/speed.bench.tsxRepository: TanStack/router Length of output: 2737 🏁 Script executed: #!/bin/bash
# Find the route definitions to understand rootRouteId and child route structure
echo "=== Looking for route definitions in tests directory ==="
rg -n "rootRouteId|createRootRoute|\$id" packages/react-router/tests/speed.bench.tsx -B 2 -A 2
echo "=== Checking if there's a routes file in the test directory ==="
fd -t f "route" packages/react-router/tests/ --max-depth 3Repository: TanStack/router Length of output: 1527 🏁 Script executed: #!/bin/bash
# Check TypeScript configuration and compilation
echo "=== TypeScript config for react-router tests ==="
find packages/react-router -name "tsconfig*.json" -type f | head -5 | xargs -I {} sh -c 'echo "File: {}"; cat {}'
echo "=== Check if this file type is included in type checking ==="
rg -n "bench.tsx|speed.bench" packages/react-router/tsconfig* 2>/dev/null || echo "Pattern not found in tsconfig files"Repository: TanStack/router Length of output: 543 🏁 Script executed: #!/bin/bash
# Look for similar useParams/useSearch patterns with from parameter in test files
echo "=== Similar useParams/useSearch patterns in tests ==="
rg -n "useParams|useSearch" packages/react-router/tests/ -A 2 | head -50Repository: TanStack/router Length of output: 3662 🏁 Script executed: #!/bin/bash
# Check if there are any type checking configurations that might exclude bench files
echo "=== Checking package.json for type checking scripts ==="
cat packages/react-router/package.json | grep -A 5 '"type\|"test\|"check' | head -30
echo "=== Looking for any tsconfig exclusions ==="
cat packages/react-router/tsconfig.json | grep -i excludeRepository: TanStack/router Length of output: 1475 🏁 Script executed: #!/bin/bash
# Check the useParams implementation to understand how select function is typed
echo "=== Finding useParams hook implementation ==="
fd -t f "useParams" packages/react-router/src --max-depth 2
echo "=== Examining useParams implementation ==="
rg -n "export.*useParams|function useParams" packages/react-router/src -A 20 | head -80Repository: TanStack/router Length of output: 3131 🏁 Script executed: #!/bin/bash
# Look for type definitions to understand select function parameter typing
echo "=== Looking for useParams type definitions ==="
fd -t f "\.d\.ts" packages/react-router/src --max-depth 3 | xargs grep -l "useParams" 2>/dev/null | head -3
echo "=== Checking if select function has implicit any ==="
ast-grep --pattern 'select: ($_) => $$$'Repository: TanStack/router Length of output: 158 🏁 Script executed: #!/bin/bash
# Check the UseParamsOptions type definition to understand select parameter typing
echo "=== Looking for UseParamsOptions type definition ==="
rg -n "UseParamsOptions|type UseParamsOptions" packages/react-router/src -A 10 | head -60
echo "=== Checking typePrimitives file for ValidateUseParamsOptions ==="
cat packages/react-router/src/typePrimitives.ts | grep -A 30 "ValidateUseParamsOptions" | head -50Repository: TanStack/router Length of output: 4651 🏁 Script executed: #!/bin/bash
# Verify which tsconfig is actually used for type checking
echo "=== Content of tsconfig.legacy.json ==="
cat packages/react-router/tsconfig.legacy.json
echo ""
echo "=== Checking root tsconfig for defaults ==="
cat tsconfig.json | head -40Repository: TanStack/router Length of output: 988 🏁 Script executed: #!/bin/bash
# Verify actual TypeScript compilation would catch this
echo "=== Trying to compile the bench file with TypeScript ==="
# Check if the file would have type errors by looking at its structure
echo "Looking at line 33 specifically where params.id is accessed:"
sed -n '30,35p' packages/react-router/tests/speed.bench.tsx
echo ""
echo "Root route definition (no params):"
sed -n '62,64p' packages/react-router/tests/speed.bench.tsx
echo ""
echo "Child route with \$id param:"
sed -n '66,70p' packages/react-router/tests/speed.bench.tsxRepository: TanStack/router Length of output: 594 🏁 Script executed: #!/bin/bash
# Check the UseParamsBaseOptions to understand the select function parameter type
echo "=== Looking for UseParamsBaseOptions definition ==="
rg -n "UseParamsBaseOptions|type UseParamsBaseOptions" packages/react-router/src -A 15 | head -80
echo ""
echo "=== Check if select parameter has explicit type ==="
rg -n "select.*\?:" packages/react-router/src/useParams.tsx -B 2 -A 2Repository: TanStack/router Length of output: 3532 🏁 Script executed: #!/bin/bash
# Confirm that test files are NOT actually type-checked by the test:types script
echo "=== Verifying type checking excludes test files ==="
echo "The test:types script uses tsconfig.legacy.json:"
cat packages/react-router/package.json | grep -A 10 '"test:types"'
echo ""
echo "And tsconfig.legacy.json only includes src:"
cat packages/react-router/tsconfig.legacy.json
echo ""
echo "But packages/react-router/tsconfig.json includes tests:"
cat packages/react-router/tsconfig.json | grep -A 5 '"include"'Repository: TanStack/router Length of output: 1192 🏁 Script executed: #!/bin/bash
# Check how ResolveUseParams resolves the type when from: rootRouteId is used
echo "=== Looking for ResolveUseParams type definition ==="
rg -n "ResolveUseParams|type ResolveUseParams" packages/react-router/src -A 15 | head -100
echo ""
echo "=== Check if there's special handling for from parameter ==="
rg -n "from.*rootRouteId|resolveFrom" packages/router-core/src -A 5 | head -40Repository: TanStack/router Length of output: 2275 🏁 Script executed: #!/bin/bash
# Double-check: is this file actually compiled in vitest or just interpreted?
echo "=== Checking vitest/tsconfig behavior for .tsx files ==="
cat packages/react-router/vite.config.ts 2>/dev/null | head -50 || echo "Checking for vitest config..."
echo ""
echo "=== Confirm benchmark file is intended for performance testing only ==="
head -30 packages/react-router/tests/speed.bench.tsxRepository: TanStack/router Length of output: 1858 🏁 Script executed: #!/bin/bash
# The key finding: vite.config.ts shows typecheck is enabled!
# This means the bench file IS type-checked. Let's check ResolveUseParams in router-core
echo "=== Checking ResolveUseParams definition in router-core ==="
fd -t f "ResolveUseParams" packages/router-core/src --max-depth 3
echo ""
echo "=== Finding the actual implementation ==="
rg -n "type ResolveUseParams|ResolveUseParams =" packages/router-core/src -A 10 | head -80Repository: TanStack/router Length of output: 905 🏁 Script executed: #!/bin/bash
# Check if rootRouteId has special typing behavior
echo "=== Checking rootRouteId definition ==="
rg -n "rootRouteId|export.*rootRouteId" packages/router-core/src -B 2 -A 5 | head -40
echo ""
echo "=== Checking how from parameter affects type resolution ==="
rg -n "from.*string.*undefined|TFrom extends" packages/router-core/src -B 2 -A 2 | head -50Repository: TanStack/router Length of output: 5750 🏁 Script executed: #!/bin/bash
# Check the actual rootRouteId constant definition
echo "=== Finding rootRouteId constant ==="
rg -n "export.*rootRouteId|const rootRouteId" packages/router-core/src -B 2 -A 5
echo ""
echo "=== Check RouteById type to understand how it resolves ==="
rg -n "type RouteById|RouteById<" packages/router-core/src -A 8 | head -60Repository: TanStack/router Length of output: 4993 🏁 Script executed: #!/bin/bash
# Most important: verify if type checking actually catches this as an error
echo "=== Try running the actual typecheck on this file ==="
cd packages/react-router
echo "Running vitest typecheck on speed.bench.tsx:"
npx vitest typecheck packages/react-router/tests/speed.bench.tsx 2>&1 || echo "Exit code: $?"Repository: TanStack/router Length of output: 1148 🏁 Script executed: #!/bin/bash
# Verify final key details about type checking
echo "=== Verify: Does tsconfig.legacy.json (used by test:types) include bench files? ==="
cat packages/react-router/tsconfig.legacy.json
echo ""
echo "=== Verify: Does vite.config.ts enable typecheck for bench files? ==="
cat packages/react-router/vite.config.ts | grep -A 5 "typecheck"
echo ""
echo "=== Summary: Check what RoutesById contains for root route ==="
rg -n "type RoutesById|RoutesById<" packages/router-core/src -A 5 | head -40Repository: TanStack/router Length of output: 2781 🏁 Script executed: #!/bin/bash
# Final verification: Can we understand what allParams resolves to for root route?
echo "=== Looking at ParseRoute and how allParams is built ==="
rg -n "allParams|'types'" packages/router-core/src -B 2 -A 2 | head -80Repository: TanStack/router Length of output: 5641 🏁 Script executed: #!/bin/bash
# Final verification: Check if there are comments or patterns suggesting this is intentional
echo "=== Check context around the Params/Search components ==="
sed -n '28,50p' packages/react-router/tests/speed.bench.tsx
echo ""
echo "=== Check if void suppression is common pattern in benchmarks ==="
rg -n "void params|void search" packages/react-router/tests/ -B 2 -A 2
echo ""
echo "=== Understand the benchmark purpose ==="
sed -n '88,110p' packages/react-router/tests/speed.bench.tsxRepository: TanStack/router Length of output: 2038 Use Using function Params() {
const params = useParams({
from: '/$id',
select: (params) => runPerfSelectorComputation(Number(params.id ?? 0)),
})
void params
return null
}
function Search() {
const search = useSearch({
from: '/$id',
select: (search) => runPerfSelectorComputation(Number(search.id ?? 0)),
})
void search
return null
}This aligns with TypeScript strict mode requirements and makes the code's intent clearer. 🤖 Prompt for AI Agents |
||
|
|
||
| function Root() { | ||
| return ( | ||
| <> | ||
| {selectors.map((selector) => ( | ||
| <Params key={selector} /> | ||
| ))} | ||
| {selectors.map((selector) => ( | ||
| <Search key={selector} /> | ||
| ))} | ||
| <Outlet /> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| const root = createRootRoute({ | ||
| component: Root, | ||
| }) | ||
|
|
||
| const route = createRoute({ | ||
| getParentRoute: () => root, | ||
| path: '/$id', | ||
| component: () => <div />, | ||
| }) | ||
|
|
||
| return createRouter({ | ||
| history: createMemoryHistory({ | ||
| initialEntries: ['/0'], | ||
| }), | ||
| scrollRestoration: true, | ||
| routeTree: root.addChildren([route]), | ||
| }) | ||
| } | ||
|
|
||
| describe('speed', () => { | ||
| let id = 0 | ||
| const router = createTestRouter() | ||
| let unsub = () => {} | ||
| let next: () => Promise<void> | ||
|
|
||
| bench('navigate', () => act(next), { | ||
| warmupIterations: 1000, | ||
| time: 10_000, | ||
| setup: async () => { | ||
| id = 0 | ||
| let resolve = () => {} | ||
| unsub = router.subscribe('onRendered', () => resolve()) | ||
|
|
||
| const navigate = (opts: NavigateOptions) => | ||
| new Promise<void>((resolveNext, rejectNext) => { | ||
| resolve = resolveNext | ||
| router.navigate(opts).catch(rejectNext) | ||
| }) | ||
|
|
||
| next = () => { | ||
| const nextId = id++ | ||
|
|
||
| return navigate({ | ||
| to: '/$id', | ||
| params: { id: nextId }, | ||
| search: { id: nextId }, | ||
| replace: true, | ||
| }) | ||
| } | ||
|
|
||
| render(<RouterProvider router={router} />) | ||
| await act(() => router.load()) | ||
| }, | ||
| teardown: () => { | ||
| cleanup() | ||
| unsub() | ||
| }, | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useParamshere is subscribed withfrom: rootRouteId, but the root route in this test has no strict path params while theidparam is defined on the child/$idroute. In this setupparams.idis alwaysundefined, sorunPerfSelectorComputation(Number(params.id ?? 0))stays constant and these 20Paramsselectors do not measure path-param recomputation during navigation, which skews the reported navigation-speed benchmark results.Useful? React with 👍 / 👎.