Skip to content

Commit 50315ac

Browse files
EfimenkoTkDodo
andauthored
fix(query-core): handle errors that occur inside setData method (#7966)
* fix(query-core): add test case for not serializable data * fix(query-core): handle `setData` error * Update packages/query-core/src/__tests__/query.test.tsx * fix(query-core): add `return` statement * fix(query-core): throw the error in case when data is not serializable for non production env * fix(query-core): delete packages/query-core/tsconfig.vitest-temp.json * fix(query-core): lift serializability check and add console.error * chore: message --------- Co-authored-by: Dominik Dorfmeister <[email protected]>
1 parent 82bfc34 commit 50315ac

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

packages/query-core/src/__tests__/query.test.tsx

+49
Original file line numberDiff line numberDiff line change
@@ -965,4 +965,53 @@ describe('query', () => {
965965
await sleep(60) // let it resolve
966966
expect(spy).toHaveBeenCalledWith('1 - 2')
967967
})
968+
969+
it('should have an error status when queryFn data is not serializable', async () => {
970+
const consoleMock = vi.spyOn(console, 'error')
971+
972+
consoleMock.mockImplementation(() => undefined)
973+
974+
const key = queryKey()
975+
976+
const queryFn = vi.fn()
977+
978+
queryFn.mockImplementation(async () => {
979+
await sleep(10)
980+
981+
const data: Array<{
982+
id: number
983+
name: string
984+
link: null | { id: number; name: string; link: unknown }
985+
}> = Array.from({ length: 5 })
986+
.fill(null)
987+
.map((_, index) => ({
988+
id: index,
989+
name: `name-${index}`,
990+
link: null,
991+
}))
992+
993+
if (data[0] && data[1]) {
994+
data[0].link = data[1]
995+
data[1].link = data[0]
996+
}
997+
998+
return data
999+
})
1000+
1001+
await queryClient.prefetchQuery({ queryKey: key, queryFn })
1002+
1003+
const query = queryCache.find({ queryKey: key })!
1004+
1005+
expect(queryFn).toHaveBeenCalledTimes(1)
1006+
1007+
expect(consoleMock).toHaveBeenCalledWith(
1008+
expect.stringContaining(
1009+
'StructuralSharing requires data to be JSON serializable',
1010+
),
1011+
)
1012+
1013+
expect(query.state.status).toBe('error')
1014+
1015+
consoleMock.mockRestore()
1016+
})
9681017
})

packages/query-core/src/query.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,12 @@ export class Query<
499499
return
500500
}
501501

502-
this.setData(data)
502+
try {
503+
this.setData(data)
504+
} catch (error) {
505+
onError(error as TError)
506+
return
507+
}
503508

504509
// Notify cache callback
505510
this.#cache.config.onSuccess?.(data, this as Query<any, any, any, any>)

packages/query-core/src/utils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,19 @@ export function replaceData<
353353
if (typeof options.structuralSharing === 'function') {
354354
return options.structuralSharing(prevData, data) as TData
355355
} else if (options.structuralSharing !== false) {
356+
if (process.env.NODE_ENV !== 'production') {
357+
try {
358+
JSON.stringify(prevData)
359+
JSON.stringify(data)
360+
} catch (error) {
361+
console.error(
362+
`StructuralSharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${options.queryHash}]: ${error}`,
363+
)
364+
365+
throw new Error('Data is not serializable')
366+
}
367+
}
368+
356369
// Structurally share data between prev and new data if needed
357370
return replaceEqualDeep(prevData, data)
358371
}

0 commit comments

Comments
 (0)