Skip to content

Commit df49fbd

Browse files
authoredOct 23, 2024··
fix(react-query): make sure promises are always finalized (#8211)
otherwise, there can be cases with multiple observers where we have listeners already, but still need to finalize
1 parent 4fceb26 commit df49fbd

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed
 

‎packages/react-query/src/__tests__/useQuery.promise.test.tsx

+60
Original file line numberDiff line numberDiff line change
@@ -964,4 +964,64 @@ describe('useQuery().promise', () => {
964964
'test0new', // fresh data, background refetch, only for latest
965965
])
966966
})
967+
968+
it('should not suspend indefinitely with multiple, nested observers)', async () => {
969+
const key = queryKey()
970+
971+
function MyComponent({ input }: { input: string }) {
972+
const query = useTheQuery(input)
973+
const data = React.use(query.promise)
974+
975+
return <>{data}</>
976+
}
977+
978+
function useTheQuery(input: string) {
979+
return useQuery({
980+
staleTime: Infinity,
981+
queryKey: [key, input],
982+
queryFn: async () => {
983+
await sleep(1)
984+
return input + ' response'
985+
},
986+
})
987+
}
988+
989+
function Page() {
990+
const [input, setInput] = React.useState('defaultInput')
991+
useTheQuery(input)
992+
993+
return (
994+
<div>
995+
<button onClick={() => setInput('someInput')}>setInput</button>
996+
<React.Suspense fallback="loading..">
997+
<MyComponent input={input} />
998+
</React.Suspense>
999+
</div>
1000+
)
1001+
}
1002+
1003+
const rendered = renderWithClient(queryClient, <Page />)
1004+
await waitFor(() => rendered.getByText('loading..'))
1005+
await waitFor(() => rendered.getByText('defaultInput response'))
1006+
1007+
expect(
1008+
queryClient.getQueryCache().find({ queryKey: [key, 'defaultInput'] })!
1009+
.observers.length,
1010+
).toBe(2)
1011+
1012+
fireEvent.click(rendered.getByText('setInput'))
1013+
1014+
await waitFor(() => rendered.getByText('loading..'))
1015+
await waitFor(() => rendered.getByText('someInput response'))
1016+
1017+
expect(
1018+
queryClient.getQueryCache().find({ queryKey: [key, 'defaultInput'] })!
1019+
.observers.length,
1020+
).toBe(0)
1021+
1022+
expect(
1023+
queryClient.getQueryCache().find({ queryKey: [key, 'someInput'] })!
1024+
.observers.length,
1025+
).toBe(2)
1026+
})
9671027
})

‎packages/react-query/src/useBaseQuery.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,8 @@ export function useBaseQuery<
150150
client.getQueryCache().get(defaultedOptions.queryHash)?.promise
151151

152152
promise?.catch(noop).finally(() => {
153-
if (!observer.hasListeners()) {
154-
// `.updateResult()` will trigger `.#currentThenable` to finalize
155-
observer.updateResult()
156-
}
153+
// `.updateResult()` will trigger `.#currentThenable` to finalize
154+
observer.updateResult()
157155
})
158156
}
159157

0 commit comments

Comments
 (0)
Please sign in to comment.