perf(react-router): cache link pathname interpolation - #8249
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 4178075
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version Preview5 package(s) bumped directly, 18 bumped as dependents. 🟩 Patch bumps
|
Bundle Size Benchmarks
The following scenarios have bundle-size changes compared with the baseline:
Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better. |
Merging this PR will degrade performance by 9.91%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | mem server error-paths not-found (vue) |
485.2 KB | 2,289.5 KB | -78.81% |
| ❌ | Memory | mem server error-paths not-found (solid) |
577.2 KB | 917.7 KB | -37.11% |
| ❌ | Memory | mem client navigation-churn (solid) |
612.8 KB | 724.7 KB | -15.43% |
| ❌ | Memory | mem client unique-location-churn (vue) |
420.2 KB | 456.6 KB | -7.97% |
| ❌ | Memory | mem server aborted-requests (react) |
798.2 KB | 848.2 KB | -5.9% |
| ❌ | Memory | mem server server-fn-churn (react) |
371.7 KB | 391.6 KB | -5.08% |
| ❌ | Memory | mem server peak-large-page (solid) |
1.1 MB | 1.1 MB | -4.2% |
| ❌ | Memory | mem client mount-unmount (solid) |
513.7 KB | 533.7 KB | -3.75% |
| ⚡ | Simulation | client-links navigation loop (react) |
149 ms | 123.3 ms | +20.82% |
| ⚡ | Simulation | client-nested-params navigation loop (react) |
149.5 ms | 130.8 ms | +14.3% |
| ⚡ | Simulation | ssr server-fn POST (solid) |
159.6 ms | 147.9 ms | +7.89% |
| ⚡ | Simulation | ssr server-fn not-found (solid) |
154 ms | 145.2 ms | +6.07% |
| ⚡ | Simulation | ssr server-fn GET (solid) |
180 ms | 170.8 ms | +5.44% |
| ⚡ | Simulation | ssr server-fn send-context (solid) |
154.7 ms | 147 ms | +5.2% |
| ⚡ | Memory | mem server error-paths redirect (react) |
317.8 KB | 302.4 KB | +5.07% |
| ⚡ | Simulation | ssr server-fn redirect (solid) |
122.1 ms | 117.5 ms | +3.87% |
| ⚡ | Simulation | client-route-tree-scale navigation loop (react) |
84.4 ms | 81.4 ms | +3.78% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing codex/cache-link-interpolation (4178075) with main (28a5e45)
There was a problem hiding this comment.
Nx Cloud is proposing a fix for your failed CI:
We corrected the search middleware in the HMR test to use the proper composition pattern: calling next(search) first and merging { rebuilt: true } onto the result, rather than passing the modified search into next. This fixes the failure because applySearchMiddleware's terminal case always returns {} when no dest.search is set, so any modifications passed into next were silently discarded. With the corrected pattern the middleware appends its key on top of the resolved base, matching the established contract used by retainSearchParams.
Tip
✅ We verified this fix by re-running @tanstack/router-core:test:unit.
diff --git a/packages/router-core/tests/build-location-cache.test.ts b/packages/router-core/tests/build-location-cache.test.ts
index 4639c097..6d2b2888 100644
--- a/packages/router-core/tests/build-location-cache.test.ts
+++ b/packages/router-core/tests/build-location-cache.test.ts
@@ -491,7 +491,7 @@ describe('buildLocation memo (_buildCache)', () => {
// `handleRouteUpdate` in router-plugin swaps route options in place and
// rebuilds the tree without touching `router.options`.
router.routesByPath['/items/$id']!.options.search = {
- middlewares: [({ search, next }) => next({ ...search, rebuilt: true })],
+ middlewares: [({ search, next }) => ({ ...next(search), rebuilt: true })],
}
router.setRoutes(router.buildRouteTree())
const rebuilt = buildBoth(router, cache, opts)
Or Apply changes locally with:
npx nx-cloud apply-locally bpnw-bITT
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
🎯 Changes
Supersedes #8229.
Mounted React links rebuild their location on navigation even when their pathname dependencies have not changed. Cache the interpolated, decoded pathname in a slot owned by each mounted link. Continue resolving params and destination routes and building the rest of the location on every call, so callbacks, search serialization, URL rewrites, masks, hash and state remain fresh.
_splatwithout creating and deleting its legacy*alias; ordinary interpolation still returns that alias.Record<string, string>.React supplies the cache slot. Solid and Vue bindings are unchanged; they still incur the shared core bundle addition and need separate reactive integration and performance validation before using it.
Results
Production bundle comparison against
c18e690814, across all 18 scenarios: +145 to +193 gzip bytes, with no additional JavaScript files.In the final focused core run, a warm literal cache took 0.15999 ms per 100 builds, versus 0.24971 ms without the slot: 35.9% less time in that fixture.
The final simplification was also measured against the preceding implementation with descriptor checks and cold-entry validation:
Final RME was ±0.10–0.16%, with 3,877–6,251 samples. The uncached control also moved, so the small warm difference should be interpreted cautiously. This measures the combined simplification, not the cost of
deletealone.For broader context, the production React navigation fixture (202 persistent links, eight navigations per sample) measured 5.3902 → 3.8739 ms, or 28.1% less time, on the preceding version before the final descriptor/alias simplification. RME was ±0.64% / ±0.59%, with 1,856 / 2,582 samples. That navigation benchmark was not rerun on this exact revision. These are local workload measurements; cold calls and changing dependencies do not receive the same benefit as warm hits.
Validation
Production builds, all 18 bundle scenarios and benchmark fixture comparisons/timings were run. Coverage was added for cache hits and invalidation, optional/inherited/splat params, callbacks, rewrites, masks, HMR middleware changes and React link updates. Unit, type and e2e suites have not been rerun for this latest revision.
✅ Checklist
🚀 Release Impact