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) }, diff --git a/packages/react-query/src/__tests__/useQuery.promise.test.tsx b/packages/react-query/src/__tests__/useQuery.promise.test.tsx index 8f06f1e165..b6c4bba173 100644 --- a/packages/react-query/src/__tests__/useQuery.promise.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.promise.test.tsx @@ -6,11 +6,13 @@ import { useTrackRenders, } from '@testing-library/react-render-stream' import { queryKey } from '@tanstack/query-test-utils' +import { waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider, QueryErrorResetBoundary, keepPreviousData, + useInfiniteQuery, useQuery, } from '..' import { QueryCache } from '../index' @@ -417,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() @@ -485,7 +487,7 @@ describe('useQuery().promise', () => { { const { renderedComponents, withinDOM } = await renderStream.takeRender() withinDOM().getByText('test') - expect(renderedComponents).toEqual([MyComponent]) + expect(renderedComponents).toEqual([Page, MyComponent]) } }) @@ -679,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') @@ -1094,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() @@ -1108,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() @@ -1282,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() @@ -1297,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 () => { @@ -1383,4 +1381,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() + } + }) })