fix: make setSearchParams return a stable reference#14803
fix: make setSearchParams return a stable reference#14803veeceey wants to merge 4 commits intoremix-run:mainfrom
Conversation
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 detectedLatest commit: ca00767 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
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 |
|
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 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 |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
Problem
setSearchParamsfromuseSearchParams()gets recreated every time the search params change, becausesearchParamsis in itsuseCallbackdependency array. This is a long-standing issue (#9991) that causes unnecessary re-renders whensetSearchParamsis 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
searchParamsvalue so thesetSearchParamscallback 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
setSearchParamsreference equality across re-rendersReact.memochild components don't re-render when only search params changeuseSearchParamstests continue to pass (637/637 DOM tests green)