Skip to content

Conversation

@HarrySeop
Copy link
Contributor

@HarrySeop HarrySeop commented Jan 16, 2026

What Changed

Modified retainSearchParams and stripSearchParams to prevent mutation of the original search object by creating shallow copies before modification.

Files Changed:

  • packages/router-core/src/searchMiddleware.ts
    • retainSearchParams: Added const copy = { ...result } before modifying
    • stripSearchParams: Changed const result = next(search) to const result = { ...next(search) }

Why

Fixes #6298

Problem:

  • When reusing the same search object reference (e.g., with useRef), the middlewares mutated the original object
  • This violated React's immutability principle and caused unexpected behavior on re-renders

Example of the bug:

const searchRef = useRef({ id: '1', filter: 'active' })

// First render
<Link search={searchRef.current} /> // { id: '1', filter: 'active' }

// After stripSearchParams(['filter']) executes
// searchRef.current was mutated to { id: '1' }

// Second render - unexpected behavior

How Tested

New Tests Added:

  • packages/router-core/tests/searchMiddleware.test.ts
    • Tests that both middlewares don't mutate original objects
    • Tests same reference reuse scenarios (the core bug case)
    • Tests all code paths (array input, object input, true input)

Test Results:

  • ✅ All new tests pass
  • ✅ All existing tests pass
  • ✅ Backward compatible (inline objects still work)

Closes #6298

Summary by CodeRabbit

  • Bug Fixes

    • Prevented unintended mutation of search parameter objects during middleware filtering and retention, ensuring original objects remain unchanged.
  • Tests

    • Added tests verifying immutability across middleware operations, repeated invocations, and both array- and object-style parameter specifications.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

The PR prevents the search middleware from mutating the original search objects: retainSearchParams now inserts missing keys into a copy of the middleware result, and stripSearchParams operates on a shallow copy before deletions, preserving immutability of objects returned by next(search).

Changes

Cohort / File(s) Summary
Search middleware
packages/router-core/src/searchMiddleware.ts
retainSearchParams now builds a separate copy of the middleware result before inserting missing keys; stripSearchParams creates a shallow copy (via spread) before applying deletions to avoid mutating the object returned by next(search).
Tests — mutation prevention
packages/router-core/tests/searchMiddleware.test.ts
Added tests ensuring retainSearchParams and stripSearchParams do not mutate original search objects across repeated invocations, boolean/array/object arg variants, and when next() returns updated search values.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • Sheraff

Poem

🐰
I nibble bugs with careful paws,
Copies kept without a cause,
No mutated crumbs left on the floor,
Search params safe forevermore,
Hoppy hops and tests that roar! 🥕🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: preventing search params mutation in middlewares, which directly addresses the core issue fixed in this PR.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from #6298: prevents retainSearchParams and stripSearchParams from mutating original search objects via shallow copies, preserves immutability semantics, maintains intended behavior, and ensures compatibility.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the objectives: modifications to searchMiddleware.ts implement immutability fixes, and new tests in searchMiddleware.test.ts validate the mutation prevention across all code paths.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@nx-cloud
Copy link

nx-cloud bot commented Jan 17, 2026

View your CI Pipeline Execution ↗ for commit 708eaeb

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

☁️ Nx Cloud last updated this comment at 2026-01-17 12:02:27 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 17, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6397

@tanstack/eslint-plugin-router

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

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6397

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6397

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6397

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6397

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6397

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6397

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6397

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6397

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6397

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6397

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6397

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6397

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6397

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6397

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6397

@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@6397

@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@6397

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6397

commit: 708eaeb

@schiller-manuel
Copy link
Contributor

test fails

@HarrySeop
Copy link
Contributor Author

test fails

While fixing TypeScript IDE errors in the test file, I realized that the test expectations were incorrect.

The tests were passing all keys to next() with empty values, but retainSearchParams only adds keys that are missing from the result (if (!(key in result))).

I updated the tests to return partial objects instead and added as any where necessary to bypass strict type checking.

This issue surfaced after adjusting the code for TypeScript errors, and I’ve confirmed that all tests are now passing.

Sorry for the confusion caused by this change.

@schiller-manuel schiller-manuel merged commit 1f59315 into TanStack:main Jan 17, 2026
6 checks passed
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.

Search middleware changes objects unexpectedly

2 participants