From 0a72df88a832946de2e4e85b9e01a22d9c771283 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Thu, 16 Oct 2025 15:31:37 +0200 Subject: [PATCH 1/3] fix(core): using the promise means we have to observer data --- .changeset/good-windows-tell.md | 5 +++++ packages/query-core/src/queryObserver.ts | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 .changeset/good-windows-tell.md diff --git a/.changeset/good-windows-tell.md b/.changeset/good-windows-tell.md new file mode 100644 index 0000000000..48e90e240b --- /dev/null +++ b/.changeset/good-windows-tell.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-core': patch +--- + +fix: observing "promise" needs to implicitly observe "data" diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 1fe6aae676..92978673f6 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -269,16 +269,18 @@ export class QueryObserver< get: (target, key) => { this.trackProp(key as keyof QueryObserverResult) onPropTracked?.(key as keyof QueryObserverResult) - if ( - key === 'promise' && - !this.options.experimental_prefetchInRender && - this.#currentThenable.status === 'pending' - ) { - this.#currentThenable.reject( - new Error( - 'experimental_prefetchInRender feature flag is not enabled', - ), - ) + if (key === 'promise') { + this.trackProp('data') + if ( + !this.options.experimental_prefetchInRender && + this.#currentThenable.status === 'pending' + ) { + this.#currentThenable.reject( + new Error( + 'experimental_prefetchInRender feature flag is not enabled', + ), + ) + } } return Reflect.get(target, key) }, From a7e66035a55b6f2c6c3ca4aef2e06d54f9f67e9b Mon Sep 17 00:00:00 2001 From: TkDodo Date: Thu, 16 Oct 2025 16:02:31 +0200 Subject: [PATCH 2/3] test --- .../src/__tests__/useQuery.promise.test.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/react-query/src/__tests__/useQuery.promise.test.tsx b/packages/react-query/src/__tests__/useQuery.promise.test.tsx index 8f06f1e165..0940341755 100644 --- a/packages/react-query/src/__tests__/useQuery.promise.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.promise.test.tsx @@ -11,6 +11,7 @@ import { QueryClientProvider, QueryErrorResetBoundary, keepPreviousData, + useInfiniteQuery, useQuery, } from '..' import { QueryCache } from '../index' @@ -1383,4 +1384,51 @@ describe('useQuery().promise', () => { .observers.length, ).toBe(2) }) + + it('should implicitly observe data when promise is used', async () => { + const key = queryKey() + + const renderStream = createRenderStream({ snapshotDOM: true }) + + function Page() { + useTrackRenders() + const query = useInfiniteQuery({ + queryKey: key, + queryFn: async () => { + await vi.advanceTimersByTimeAsync(1) + return { nextCursor: 1, data: 'test' } + }, + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }) + + React.use(query.promise) + + const hasNextPage = query.hasNextPage + + return ( +
+
hasNextPage: {String(hasNextPage)}
+
+ ) + } + + await renderStream.render( + + + + + , + ) + + { + const { withinDOM } = await renderStream.takeRender() + expect(withinDOM().getByText('loading..')).toBeInTheDocument() + } + + { + const { withinDOM } = await renderStream.takeRender() + expect(withinDOM().getByText('hasNextPage: true')).toBeInTheDocument() + } + }) }) From e6fce973a626250ee27f6d694b5f71ed68215446 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Thu, 16 Oct 2025 16:18:48 +0200 Subject: [PATCH 3/3] chore: fix other tests --- .../src/__tests__/useQuery.promise.test.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/react-query/src/__tests__/useQuery.promise.test.tsx b/packages/react-query/src/__tests__/useQuery.promise.test.tsx index 0940341755..b6c4bba173 100644 --- a/packages/react-query/src/__tests__/useQuery.promise.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.promise.test.tsx @@ -6,6 +6,7 @@ import { useTrackRenders, } from '@testing-library/react-render-stream' import { queryKey } from '@tanstack/query-test-utils' +import { waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider, @@ -418,7 +419,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test-0') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } rendered.getByRole('button', { name: 'increment' }).click() @@ -486,7 +487,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } }) @@ -680,7 +681,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test1') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } queryClient.setQueryData(key, 'test2') @@ -1095,7 +1096,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test0') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } rendered.getByText('inc').click() @@ -1109,7 +1110,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test1') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } rendered.getByText('dec').click() @@ -1283,13 +1284,13 @@ describe('useQuery().promise', () => { rendered.getByText('dec').click() { const { snapshot } = await renderStream.takeRender() - expect(snapshot).toMatchObject({ data: 'test2' }) + expect(snapshot).toMatchObject({ data: 'test3' }) } rendered.getByText('dec').click() { const { snapshot } = await renderStream.takeRender() - expect(snapshot).toMatchObject({ data: 'test1' }) + expect(snapshot).toMatchObject({ data: 'test3' }) } rendered.getByText('dec').click() @@ -1298,11 +1299,7 @@ describe('useQuery().promise', () => { expect(snapshot).toMatchObject({ data: 'test0' }) } - { - const { snapshot, withinDOM } = await renderStream.takeRender() - withinDOM().getByText('test0new') - expect(snapshot).toMatchObject({ data: 'test0new' }) - } + await waitFor(() => rendered.getByText('test0new')) }) it('should not suspend indefinitely with multiple, nested observers)', async () => {