Skip to content

Comments

fix: make setSearchParams return a stable reference#14803

Open
veeceey wants to merge 4 commits intoremix-run:mainfrom
veeceey:fix/issue-9991-stable-setSearchParams
Open

fix: make setSearchParams return a stable reference#14803
veeceey wants to merge 4 commits intoremix-run:mainfrom
veeceey:fix/issue-9991-stable-setSearchParams

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 15, 2026

Problem

setSearchParams from useSearchParams() gets recreated every time the search params change, because searchParams is in its useCallback dependency array. This is a long-standing issue (#9991) that causes unnecessary re-renders when setSearchParams is passed as a prop to memoized child components.

This is inconsistent with React's useState, where the setter is always a stable reference.

Solution

Use a ref to track the current searchParams value so the setSearchParams callback can always read the latest params without depending on them directly. The dependency array goes from [navigate, searchParams] to just [navigate], making the returned setter stable across re-renders.

The functional update form (setSearchParams(prev => ...)) still receives the correct latest value via the ref.

Test plan

  • Added test verifying setSearchParams reference equality across re-renders
  • Added test verifying React.memo child components don't re-render when only search params change
  • Added test verifying functional updates still get the latest search params after the fix
  • All existing useSearchParams tests continue to pass (637/637 DOM tests green)

setSearchParams was being recreated on every search param change because
searchParams was included in its useCallback dependency array. This caused
unnecessary re-renders in child components that received setSearchParams as
a prop, even when those children didn't depend on the search params value.

The fix uses a ref to track the current searchParams, allowing the callback
to always access the latest value without being a dependency. This matches
the behavior of React's useState setter which is always stable.

Fixes remix-run#9991
@changeset-bot
Copy link

changeset-bot bot commented Feb 15, 2026

🦋 Changeset detected

Latest commit: ca00767

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
react-router Patch
@react-router/architect Patch
@react-router/cloudflare Patch
@react-router/dev Patch
react-router-dom Patch
@react-router/express Patch
@react-router/node Patch
@react-router/serve Patch
@react-router/fs-routes Patch
@react-router/remix-routes-option-adapter Patch
create-react-router Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Feb 15, 2026

Hi @veeceey,

Welcome, and thank you for contributing to React Router!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run.

Thanks!

- The Remix team

veeceey and others added 2 commits February 15, 2026 02:23
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Feb 15, 2026

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

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