Skip to content

fix(toolkit): prevent infinite refetch loop when query args contain NaN - #5339

Open
aagostino31 wants to merge 1 commit into
reduxjs:masterfrom
aagostino31:fix-structural-sharing-nan-refetch-loop
Open

fix(toolkit): prevent infinite refetch loop when query args contain NaN#5339
aagostino31 wants to merge 1 commit into
reduxjs:masterfrom
aagostino31:fix-structural-sharing-nan-refetch-loop

Conversation

@aagostino31

@aagostino31 aagostino31 commented Jul 16, 2026

Copy link
Copy Markdown

Fixes #5338

AI warning - I certainly fixed this with Claude, but I did go through a lot of manual testing and verification for this.

The problem

If the argument passed to a useQuery hook contains NaN and the request returns an error, the hook dispatches the same query again in an endless loop. Each rejection causes a rerender and each rerender dispatches a new request, so the loop runs for as long as the hook is mounted, paced only by network latency.

This is easy to hit in practice. Our case was a URL where a route param placeholder was never substituted, so the page received the literal string "worker_id", and Number("worker_id") produced NaN which then flowed into a query arg.

The regression was introduced in 2.9.0 when useStableQueryArgs switched from comparing serialized args to reference checks via copyWithStructuralSharing. On earlier versions the same code fails once and stops.

The cause

copyWithStructuralSharing decides whether the new object is identical to the old one using strict equality:

if (isSameObject) isSameObject = oldObj[key] === mergeObj[key]

NaN === NaN is false, so an arg object containing NaN is never considered equal to the previous one and useStableQueryArgs returns a new object on every render. useQuerySubscription then sees lastPromise.arg !== stableArg on every render and dispatches initiate again. While the query is in an error state, the thunk condition lets the dispatch through because the cache entry has no fulfilledTimeStamp, so every one of these dispatches reaches the network:

rejected -> rerender -> new stableArg reference -> initiate -> request -> rejected -> ...

The serialized cache key is unaffected the whole time, since JSON.stringify turns NaN into null, so all of this happens on a single cache entry.

The fix

Compare values with Object.is, which treats NaN as equal to itself. One line in copyWithStructuralSharing.

Tests

  • Two unit tests for copyWithStructuralSharing: objects containing NaN preserve identity when equal, and unchanged branches containing NaN are still shared when a sibling value changes.
  • Two hook level regression tests: an arg object containing NaN and a bare NaN arg, each asserting the base query runs exactly once when the query errors. Before the fix the object case makes 27 requests in 150ms against a 5ms mock; after the fix it makes exactly one.

The bare NaN case already behaved correctly before this change because React compares effect dependencies with Object.is, so a primitive NaN dep never retriggers the subscription effect. The test is included to lock that behavior in.

No public types change, so the API report is untouched. yarn test for the toolkit package passes: 88 files, 1320 tests, no type errors.

🤖 Generated with Claude Code

copyWithStructuralSharing compared values with ===, so NaN never equaled
itself and an arg object containing NaN got a new identity on every
render. useQuerySubscription treats that as an arg change and dispatches
initiate again. While the query is in an error state the thunk condition
allows the dispatch through, so every rejection triggers a rerender which
dispatches a new request, refetching in a loop for as long as the hook is
mounted. Comparing with Object.is treats NaN as equal to itself and
restores a stable arg identity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codesandbox

codesandbox Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for redux-starter-kit-docs ready!

Name Link
🔨 Latest commit 6e534f7
🔍 Latest deploy log https://app.netlify.com/projects/redux-starter-kit-docs/deploys/6a58f0310bc3880008f977c8
😎 Deploy Preview https://deploy-preview-5339--redux-starter-kit-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@aryaemami59 aryaemami59 added the RTK-Query Issues related to Redux-Toolkit-Query label Jul 26, 2026
@aryaemami59 aryaemami59 changed the title fix(query): prevent infinite refetch loop when query args contain NaN fix(toolkit): prevent infinite refetch loop when query args contain NaN Jul 26, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

@reduxjs/rtk-codemods

npm i https://pkg.pr.new/@reduxjs/rtk-codemods@6e534f7 -D
yarn add https://pkg.pr.new/@reduxjs/rtk-codemods@6e534f7.tgz -D
pnpm add https://pkg.pr.new/@reduxjs/rtk-codemods@6e534f7.tgz -D
bun add https://pkg.pr.new/@reduxjs/rtk-codemods@6e534f7.tgz -D

@rtk-query/codegen-openapi

npm i https://pkg.pr.new/@rtk-query/codegen-openapi@6e534f7 -D
yarn add https://pkg.pr.new/@rtk-query/codegen-openapi@6e534f7.tgz -D
pnpm add https://pkg.pr.new/@rtk-query/codegen-openapi@6e534f7.tgz -D
bun add https://pkg.pr.new/@rtk-query/codegen-openapi@6e534f7.tgz -D

@rtk-query/graphql-request-base-query

npm i https://pkg.pr.new/@rtk-query/graphql-request-base-query@6e534f7 -D
yarn add https://pkg.pr.new/@rtk-query/graphql-request-base-query@6e534f7.tgz -D
pnpm add https://pkg.pr.new/@rtk-query/graphql-request-base-query@6e534f7.tgz -D
bun add https://pkg.pr.new/@rtk-query/graphql-request-base-query@6e534f7.tgz -D

@reduxjs/toolkit

npm i https://pkg.pr.new/@reduxjs/toolkit@6e534f7 -D
yarn add https://pkg.pr.new/@reduxjs/toolkit@6e534f7.tgz -D
pnpm add https://pkg.pr.new/@reduxjs/toolkit@6e534f7.tgz -D
bun add https://pkg.pr.new/@reduxjs/toolkit@6e534f7.tgz -D

commit: 6e534f7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RTK-Query Issues related to Redux-Toolkit-Query

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useQuery refetches in an infinite loop when query args contain NaN and the request fails

2 participants