Skip to content

perf(solid-router): drop deep equality in selector hooks#7093

Closed
Sheraff wants to merge 1 commit intomainfrom
opencode/cosmic-rocket
Closed

perf(solid-router): drop deep equality in selector hooks#7093
Sheraff wants to merge 1 commit intomainfrom
opencode/cosmic-rocket

Conversation

@Sheraff
Copy link
Copy Markdown
Contributor

@Sheraff Sheraff commented Apr 2, 2026

DO NOT MERGE, I JUST OPENED THE PR TO GET CODSPEED MEASUREMENTS

Summary

  • remove replaceEqualDeep from the hot-path Solid selector hooks in useRouterState, useLocation, useMatch, and useMatches
  • keep the head tag dedupe path unchanged, since that one still stabilizes rendered head assets rather than only suppressing signal invalidation
  • make it easier to validate in CI whether dropping these structural-sharing passes improves the stable @benchmarks/client-nav Solid benchmark

Testing

  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/solid-router:test:unit --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/solid-router:test:types --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf:solid --outputStyle=stream --skipRemoteCache

Summary by CodeRabbit

  • Refactor
    • Simplified memoization logic in core router hooks (useMatches, useLocation, useMatch, useRouterState) by streamlining internal value stabilization. No changes to public APIs or user-facing behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Removed replaceEqualDeep import and deep-equality-based value stabilization from four solid-router hooks (useMatches, useLocation, useMatch, useRouterState). The memoization callbacks now recompute selected values directly without preserving previous results via deep-equality checks.

Changes

Cohort / File(s) Summary
Solid Router Hook Simplification
packages/solid-router/src/Matches.tsx, packages/solid-router/src/useLocation.tsx, packages/solid-router/src/useMatch.tsx, packages/solid-router/src/useRouterState.tsx
Removed replaceEqualDeep import and prev-based memo callbacks across all hooks. Simplified memoization logic to always return newly computed selected values instead of conditionally preserving previous results via deep-equality comparison.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 No more comparing what came before,
Deep equality checks we don't need anymore,
Memos grow simpler, lighter, more spry,
Fresh values cascade—no need to ask why,
Our hooks now dance with elegance and ease! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: removing deep equality checks in selector hooks for performance optimization in solid-router.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch opencode/cosmic-rocket

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud bot commented Apr 2, 2026

View your CI Pipeline Execution ↗ for commit f58f442

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 5m 30s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 44s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-02 19:30:12 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🚀 Changeset Version Preview

No changeset entries found. Merging this PR will not cause a version bump for any packages.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Bundle Size Benchmarks

  • Commit: 5a81726f0a2f
  • Measured at: 2026-04-02T19:25:28.514Z
  • Baseline source: history:796406da66cf
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 87.48 KiB 0 B (0.00%) 275.76 KiB 75.97 KiB ████▁▁▁▁▁▂▃
react-router.full 90.78 KiB 0 B (0.00%) 286.95 KiB 78.95 KiB ▆▆▆█▁▁▁▁▁▂▂
solid-router.minimal 35.54 KiB -16 B (-0.04%) 107.22 KiB 31.95 KiB ████▁▁▁▁▁▂█▆
solid-router.full 39.98 KiB -54 B (-0.13%) 120.67 KiB 35.88 KiB ████▂▂▂▂▂▃▆▁
vue-router.minimal 53.38 KiB 0 B (0.00%) 153.07 KiB 47.94 KiB ████▁▁▁▁▁▂▄
vue-router.full 58.25 KiB 0 B (0.00%) 168.53 KiB 52.18 KiB ████▁▁▁▁▁▂▄
react-start.minimal 102.01 KiB 0 B (0.00%) 324.00 KiB 88.21 KiB ▅▅▅█▁▂▂▂▂▃▃
react-start.full 105.38 KiB 0 B (0.00%) 334.35 KiB 91.14 KiB ▆▆▆█▁▁▁▂▂▃▃
solid-start.minimal 49.65 KiB -11 B (-0.02%) 153.47 KiB 43.74 KiB ▇▇▇▇▁▁▁▂▂▂█▇
solid-start.full 55.12 KiB -50 B (-0.09%) 169.62 KiB 48.51 KiB ▇▇▇▇▁▂▂▂▂▃█▄

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 2, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7093

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/@tanstack/eslint-plugin-router@7093

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7093

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/@tanstack/nitro-v2-vite-plugin@7093

@tanstack/react-router

npm i https://pkg.pr.new/@tanstack/react-router@7093

@tanstack/react-router-devtools

npm i https://pkg.pr.new/@tanstack/react-router-devtools@7093

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/@tanstack/react-router-ssr-query@7093

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7093

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7093

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7093

@tanstack/router-cli

npm i https://pkg.pr.new/@tanstack/router-cli@7093

@tanstack/router-core

npm i https://pkg.pr.new/@tanstack/router-core@7093

@tanstack/router-devtools

npm i https://pkg.pr.new/@tanstack/router-devtools@7093

@tanstack/router-devtools-core

npm i https://pkg.pr.new/@tanstack/router-devtools-core@7093

@tanstack/router-generator

npm i https://pkg.pr.new/@tanstack/router-generator@7093

@tanstack/router-plugin

npm i https://pkg.pr.new/@tanstack/router-plugin@7093

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/@tanstack/router-ssr-query-core@7093

@tanstack/router-utils

npm i https://pkg.pr.new/@tanstack/router-utils@7093

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/@tanstack/router-vite-plugin@7093

@tanstack/solid-router

npm i https://pkg.pr.new/@tanstack/solid-router@7093

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/@tanstack/solid-router-devtools@7093

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/@tanstack/solid-router-ssr-query@7093

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7093

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7093

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7093

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7093

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7093

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7093

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7093

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/@tanstack/start-static-server-functions@7093

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7093

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7093

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7093

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@7093

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@7093

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@7093

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7093

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7093

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7093

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7093

commit: f58f442

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/solid-router/src/useMatch.tsx (1)

109-116: Skip the memo in the default useMatch() path.

After this change, the !opts.select branch is only forwarding match(). Returning match directly would align this hook with the existing fast paths in useLocation() / useRouterState() and keep the common case off an extra memo node.

♻️ Suggested refactor
   Solid.createEffect(() => {
     if (match() !== undefined) {
       return
     }
@@
       invariant()
     }
   })
 
+  if (!opts.select) {
+    return match as Solid.Accessor<
+      ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>
+    >
+  }
+
   return Solid.createMemo(() => {
     const selectedMatch = match()
 
     if (selectedMatch === undefined) return undefined
-    return (opts.select ? opts.select(selectedMatch as any) : selectedMatch) as
-      | TSelected
-      | undefined
+    return opts.select(selectedMatch as any) as TSelected | undefined
   }) as any
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/src/useMatch.tsx` around lines 109 - 116, The current
useMatch() always wraps the result in Solid.createMemo, but when opts.select is
falsy we should skip creating an extra memo and return match() directly; update
the implementation in useMatch to check if opts.select is provided and only call
Solid.createMemo when it is, otherwise return match() (cast to the expected
type) so the fast path avoids the extra memo node and aligns with
useLocation()/useRouterState() behavior.
packages/solid-router/src/Matches.tsx (1)

217-222: Fast-path the default useMatches() case.

This still allocates a memo even when no selector is provided, while useLocation() and useRouterState() already expose the underlying store accessor directly. Given the perf goal of this PR, it seems worth skipping that extra reactive node here too.

♻️ Suggested refactor
 export function useMatches<
   TRouter extends AnyRouter = RegisteredRouter,
   TSelected = unknown,
 >(
   opts?: UseMatchesBaseOptions<TRouter, TSelected>,
 ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
   const router = useRouter<TRouter>()
+  if (!opts?.select) {
+    return (() => router.stores.activeMatchesSnapshot.state) as Solid.Accessor<
+      UseMatchesResult<TRouter, TSelected>
+    >
+  }
+
+  const select = opts.select
   return Solid.createMemo(() => {
     const matches = router.stores.activeMatchesSnapshot.state as Array<
       MakeRouteMatchUnion<TRouter>
     >
-    return (opts?.select ? opts.select(matches) : matches) as any
+    return select(matches) as UseMatchesResult<TRouter, TSelected>
   }) as Solid.Accessor<UseMatchesResult<TRouter, TSelected>>
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/src/Matches.tsx` around lines 217 - 222, The current
useMatches implementation always wraps the store accessor in Solid.createMemo
which allocates an unnecessary reactive node when no selector is passed; change
useMatches to fast-path the default case by returning the underlying store
accessor directly when opts?.select is falsy (i.e. return a Solid.Accessor that
reads router.stores.activeMatchesSnapshot.state and casts to
UseMatchesResult/MakeRouteMatchUnion) and only call Solid.createMemo when
opts?.select is provided (apply the selector to the matches inside the memo).
Target symbols: useMatches, Solid.createMemo,
router.stores.activeMatchesSnapshot.state, opts?.select, UseMatchesResult,
MakeRouteMatchUnion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/solid-router/src/useRouterState.tsx`:
- Around line 56-58: The change in useRouterState (and the identical changes in
useLocation and useMatch) removed the previous-value structural sharing via
replaceEqualDeep causing selectors that allocate new objects/arrays to always
invalidate downstream; update the release notes documenting this as a
breaking/behavioral change and add regression tests that assert selector
stability (not just call counts) — modify or add tests alongside
store-updates-during-navigation.test.tsx to verify that selectors returning
structurally-equal values do not cause re-renders, and/or restore/offer an
opt-in to replaceEqualDeep behavior in useRouterState/useLocation/useMatch by
referencing the replaceEqualDeep logic so consumers can opt back into
deep-equality sharing if required.

---

Nitpick comments:
In `@packages/solid-router/src/Matches.tsx`:
- Around line 217-222: The current useMatches implementation always wraps the
store accessor in Solid.createMemo which allocates an unnecessary reactive node
when no selector is passed; change useMatches to fast-path the default case by
returning the underlying store accessor directly when opts?.select is falsy
(i.e. return a Solid.Accessor that reads
router.stores.activeMatchesSnapshot.state and casts to
UseMatchesResult/MakeRouteMatchUnion) and only call Solid.createMemo when
opts?.select is provided (apply the selector to the matches inside the memo).
Target symbols: useMatches, Solid.createMemo,
router.stores.activeMatchesSnapshot.state, opts?.select, UseMatchesResult,
MakeRouteMatchUnion.

In `@packages/solid-router/src/useMatch.tsx`:
- Around line 109-116: The current useMatch() always wraps the result in
Solid.createMemo, but when opts.select is falsy we should skip creating an extra
memo and return match() directly; update the implementation in useMatch to check
if opts.select is provided and only call Solid.createMemo when it is, otherwise
return match() (cast to the expected type) so the fast path avoids the extra
memo node and aligns with useLocation()/useRouterState() behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68171639-179c-4456-b083-69f993b3e673

📥 Commits

Reviewing files that changed from the base of the PR and between 5a81726 and f58f442.

📒 Files selected for processing (4)
  • packages/solid-router/src/Matches.tsx
  • packages/solid-router/src/useLocation.tsx
  • packages/solid-router/src/useMatch.tsx
  • packages/solid-router/src/useRouterState.tsx

Comment on lines +56 to +58
return Solid.createMemo(() =>
select(router.stores.__store.state),
) as Accessor<UseRouterStateResult<TRouter, TSelected>>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "useRouterState.tsx" | head -20

Repository: TanStack/router

Length of output: 197


🏁 Script executed:

git ls-files | grep -E "solid-router.*src" | head -30

Repository: TanStack/router

Length of output: 2079


🏁 Script executed:

cat -n packages/solid-router/src/useRouterState.tsx 2>/dev/null | head -100

Repository: TanStack/router

Length of output: 2294


🏁 Script executed:

cd packages/solid-router && git log --oneline -20 -- src/useRouterState.tsx

Repository: TanStack/router

Length of output: 125


🏁 Script executed:

cd packages/solid-router && git diff HEAD~5..HEAD -- src/useRouterState.tsx 2>/dev/null | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

rg -l "useRouterState|useRouterStateShallow|useSearch|useParams" packages/solid-router/src/ --type ts --type tsx

Repository: TanStack/router

Length of output: 86


🏁 Script executed:

rg "replaceEqualDeep|structural.*shar|createMemo" packages/solid-router/src/ -A 2 -B 2

Repository: TanStack/router

Length of output: 17448


🏁 Script executed:

find packages/solid-router -name "*.test.*" -o -name "*.spec.*" | head -10

Repository: TanStack/router

Length of output: 558


🏁 Script executed:

cd packages/solid-router && git show f58f442a6:src/useRouterState.tsx | head -80

Repository: TanStack/router

Length of output: 276


🏁 Script executed:

cd packages/solid-router && git show f58f442a6 -- src/useRouterState.tsx src/useLocation.tsx src/useMatch.tsx

Repository: TanStack/router

Length of output: 7048


🏁 Script executed:

cd packages/solid-router && git show f58f442a6^:src/useRouterState.tsx 2>/dev/null | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cd packages/solid-router && git show f58f442a6^:src/useLocation.tsx 2>/dev/null | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cd packages/solid-router && git log --oneline -1 f58f442a6^

Repository: TanStack/router

Length of output: 251


🏁 Script executed:

cd packages/solid-router && git log --all --oneline -- src/useRouterState.tsx src/useLocation.tsx src/useMatch.tsx | head -10

Repository: TanStack/router

Length of output: 716


🏁 Script executed:

cd packages/solid-router && git log --all --oneline | head -20

Repository: TanStack/router

Length of output: 1227


🏁 Script executed:

rg "useRouterStateShallow|useSearchParams|useParams" packages/solid-router/src/ -l

Repository: TanStack/router

Length of output: 253


🏁 Script executed:

cat packages/solid-router/src/index.tsx | head -100

Repository: TanStack/router

Length of output: 1987


🏁 Script executed:

cd packages/solid-router && git show c9e18555f:src/useRouterState.tsx 2>/dev/null | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cd packages/solid-router && git log --all --format="%H %s" -- src/useRouterState.tsx src/useLocation.tsx src/useMatch.tsx | head -20

Repository: TanStack/router

Length of output: 1026


🏁 Script executed:

cd packages/solid-router && git show c9e18555f --stat | grep -E "useRouterState|useLocation|useMatch"

Repository: TanStack/router

Length of output: 254


🏁 Script executed:

rg "replaceEqualDeep" packages/solid-router/src/ -B 3 -A 3

Repository: TanStack/router

Length of output: 1369


🏁 Script executed:

cd packages/solid-router && git show c9e18555f:src/useRouterState.tsx 2>/dev/null

Repository: TanStack/router

Length of output: 43


🏁 Script executed:

cd packages/solid-router && git diff c9e18555f f58f442a6 -- src/useRouterState.tsx src/useLocation.tsx src/useMatch.tsx | head -200

Repository: TanStack/router

Length of output: 2962


🏁 Script executed:

cat packages/solid-router/tests/*.test.tsx | grep -A 10 -B 5 "useRouterState\|selector" | head -100

Repository: TanStack/router

Length of output: 1125


🏁 Script executed:

cd packages/solid-router && git show c9e18555f:src/useRouterState.tsx 2>/dev/null || echo "File may have been newly created in this commit"

Repository: TanStack/router

Length of output: 106


🏁 Script executed:

cd packages/solid-router && git log --all --format="%H %s" -- src/useRouterState.tsx | head -5

Repository: TanStack/router

Length of output: 540


🏁 Script executed:

cd packages/solid-router && git diff 054523900 c9e18555f -- src/useRouterState.tsx | head -100

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

rg "useRouterState.*select|selector" packages/solid-router/tests/ -A 5 -B 2

Repository: TanStack/router

Length of output: 846


🏁 Script executed:

cd packages/solid-router && git show 054523900:src/useRouterState.tsx 2>/dev/null | head -80

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

find . -name "*.changeset" -o -name "CHANGELOG*" | xargs grep -l "solid-router\|selector" 2>/dev/null | head -5

Repository: TanStack/router

Length of output: 274


🏁 Script executed:

cat .changeset/*.md 2>/dev/null | grep -A 10 -B 2 "solid-router\|selector\|deep.*equal" | head -80

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cd packages/solid-router && git log --all --oneline | grep -i "selector\|deep.*equal\|shallow" | head -10

Repository: TanStack/router

Length of output: 248


🏁 Script executed:

cd packages/solid-router && git show 054523900:src/useRouterState.tsx 2>/dev/null | tail -50

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

ls -la .changeset/ 2>/dev/null | head -20

Repository: TanStack/router

Length of output: 230


🏁 Script executed:

git log --all --oneline f58f442a6^..f58f442a6 2>/dev/null | head -5

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cat packages/solid-router/CHANGELOG.md | head -100

Repository: TanStack/router

Length of output: 3184


🏁 Script executed:

cd packages/solid-router && git log --all --oneline --grep="selector\|deep.*equal" | head -10

Repository: TanStack/router

Length of output: 248


🏁 Script executed:

rg "Solid.createMemo" packages/solid-router/src/ | grep -E "prev|equal|shallow" | head -20

Repository: TanStack/router

Length of output: 180


🏁 Script executed:

cat packages/solid-router/tests/store-updates-during-navigation.test.tsx

Repository: TanStack/router

Length of output: 8711


🏁 Script executed:

rg "breaking\|BREAKING\|v[0-9]" packages/solid-router/CHANGELOG.md | head -20

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cd packages/solid-router && git log --format="%B" -1 f58f442a6

Repository: TanStack/router

Length of output: 116


🏁 Script executed:

rg "selector\|shallow\|deep.*equal" packages/solid-router/CHANGELOG.md | head -20

Repository: TanStack/router

Length of output: 41


Document the selector invalidation semantics change before merging.

Lines 56–58 remove the previous-value structural sharing that used replaceEqualDeep. This means selectors allocating fresh objects or arrays will now trigger downstream invalidations on every router-store update, even when selected fields are unchanged. This is an observable API contract change, so it requires:

  • Explicit documentation in the release notes as a breaking change or major behavioral shift
  • Regression tests confirming selector stability behavior (the existing call-count assertions in store-updates-during-navigation.test.tsx verify invocation counts, not stability)

The same concern applies to useLocation and useMatch, which have identical changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/src/useRouterState.tsx` around lines 56 - 58, The
change in useRouterState (and the identical changes in useLocation and useMatch)
removed the previous-value structural sharing via replaceEqualDeep causing
selectors that allocate new objects/arrays to always invalidate downstream;
update the release notes documenting this as a breaking/behavioral change and
add regression tests that assert selector stability (not just call counts) —
modify or add tests alongside store-updates-during-navigation.test.tsx to verify
that selectors returning structurally-equal values do not cause re-renders,
and/or restore/offer an opt-in to replaceEqualDeep behavior in
useRouterState/useLocation/useMatch by referencing the replaceEqualDeep logic so
consumers can opt back into deep-equality sharing if required.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 2, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing opencode/cosmic-rocket (f58f442) with main (796406d)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (5a81726) during the generation of this report, so 796406d was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Sheraff Sheraff marked this pull request as draft April 2, 2026 19:48
@Sheraff Sheraff closed this Apr 2, 2026
@Sheraff Sheraff deleted the opencode/cosmic-rocket branch April 2, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant