From b183743e0e4cfe574ed404836b2c97e1e8df6c44 Mon Sep 17 00:00:00 2001 From: alex-js-ltd Date: Tue, 24 Mar 2026 14:39:10 +0000 Subject: [PATCH 1/3] fix: use Object.is instead of === in replaceEqualDeep for NaN equality --- packages/query-core/src/__tests__/utils.test.tsx | 6 ++++++ packages/query-core/src/utils.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/query-core/src/__tests__/utils.test.tsx b/packages/query-core/src/__tests__/utils.test.tsx index 9cee1b46424..7b051b90f8e 100644 --- a/packages/query-core/src/__tests__/utils.test.tsx +++ b/packages/query-core/src/__tests__/utils.test.tsx @@ -416,6 +416,12 @@ describe('core/utils', () => { expect(next).toBe(current) }) + + it('should return the previous object reference when values contain NaN', () => { + const prev = { value: NaN } + const next = { value: NaN } + expect(replaceEqualDeep(prev, next)).toBe(prev) + }) }) describe('matchMutation', () => { diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index b29e8ded456..109d02d11f1 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -269,7 +269,7 @@ const hasOwn = Object.prototype.hasOwnProperty */ export function replaceEqualDeep(a: unknown, b: T, depth?: number): T export function replaceEqualDeep(a: any, b: any, depth = 0): any { - if (a === b) { + if (Object.is(a, b)) { return a } @@ -292,7 +292,7 @@ export function replaceEqualDeep(a: any, b: any, depth = 0): any { const aItem = a[key] const bItem = b[key] - if (aItem === bItem) { + if (Object.is(aItem, bItem)) { copy[key] = aItem if (array ? i < aSize : hasOwn.call(a, key)) equalItems++ continue From 9a33a6872c6ceafe8303a7b818fde9a116886321 Mon Sep 17 00:00:00 2001 From: alex-js-ltd Date: Tue, 24 Mar 2026 14:44:12 +0000 Subject: [PATCH 2/3] chore: add changeset --- .changeset/happy-banks-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/happy-banks-repeat.md diff --git a/.changeset/happy-banks-repeat.md b/.changeset/happy-banks-repeat.md new file mode 100644 index 00000000000..18d0e1f53bc --- /dev/null +++ b/.changeset/happy-banks-repeat.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-core': patch +--- + +fix: use Object.is instead of === in replaceEqualDeep to correctly handle NaN equality From d5fb45be1fc7cdde68efdb032d0f439012dd714c Mon Sep 17 00:00:00 2001 From: alex-js-ltd Date: Tue, 24 Mar 2026 15:14:56 +0000 Subject: [PATCH 3/3] test: add NaN structural sharing test cases for replaceEqualDeep --- packages/query-core/src/__tests__/utils.test.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/query-core/src/__tests__/utils.test.tsx b/packages/query-core/src/__tests__/utils.test.tsx index 7b051b90f8e..f70069a3631 100644 --- a/packages/query-core/src/__tests__/utils.test.tsx +++ b/packages/query-core/src/__tests__/utils.test.tsx @@ -417,11 +417,21 @@ describe('core/utils', () => { expect(next).toBe(current) }) - it('should return the previous object reference when values contain NaN', () => { + it('preserves reference when objects contain NaN', () => { const prev = { value: NaN } const next = { value: NaN } expect(replaceEqualDeep(prev, next)).toBe(prev) }) + + it('handles top-level NaN equality', () => { + expect(Number.isNaN(replaceEqualDeep(NaN, NaN))).toBe(true) + }) + + it('preserves reference for arrays containing NaN', () => { + const prev = [NaN, { a: 1 }] + const next = [NaN, { a: 1 }] + expect(replaceEqualDeep(prev, next)).toBe(prev) + }) }) describe('matchMutation', () => {