Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions src/react-query/hooks/__tests__/useQueryData.refetch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import {QueryClient, useInfiniteQuery, useQuery} from '@tanstack/react-query';
import {renderHook} from '@testing-library/react';

import {idle} from '../../../core';
import type {AnyInfiniteQueryDataSource} from '../../impl/infinite/types';
import type {AnyPlainQueryDataSource} from '../../impl/plain/types';
import {warnDisabledRefetch} from '../../utils/warnDisabledRefetch';
import {useQueryContext} from '../useQueryContext';
import {useQueryData} from '../useQueryData';

jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
useQuery: jest.fn(),
useInfiniteQuery: jest.fn(),
}));

jest.mock('../useQueryContext');
jest.mock('../../utils/warnDisabledRefetch');

const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>;
const mockUseInfiniteQuery = useInfiniteQuery as jest.MockedFunction<typeof useInfiniteQuery>;
const mockWarnDisabledRefetch = warnDisabledRefetch as jest.MockedFunction<
typeof warnDisabledRefetch
>;

describe('useQueryData refetch behavior', () => {
const mockQueryClient = new QueryClient();
const mockContext = {queryClient: mockQueryClient};

beforeEach(() => {
jest.clearAllMocks();
(useQueryContext as jest.Mock).mockReturnValue(mockContext);
});

const createMockQueryResult = (refetch = jest.fn()) => ({
data: 'test-data',
error: null,
status: 'success' as const,
fetchStatus: 'idle' as const,
isLoading: false,
isError: false,
isSuccess: true,
isPending: false,
refetch,
dataUpdatedAt: Date.now(),
errorUpdatedAt: 0,
failureCount: 0,
failureReason: null,
isFetched: true,
isFetchedAfterMount: true,
isFetching: false,
isInitialLoading: false,
isLoadingError: false,
isPaused: false,
isPlaceholderData: false,
isPreviousData: false,
isRefetchError: false,
isRefetching: false,
isStale: false,
promise: Promise.resolve('test-data'),
});

const createMockInfiniteResult = (refetch = jest.fn()) => ({
data: {pages: [['item1'], ['item2']], pageParams: [undefined, 'next-page']},
error: null,
status: 'success' as const,
fetchStatus: 'idle' as const,
isLoading: false,
isError: false,
isSuccess: true,
isPending: false,
refetch,
hasNextPage: false,
hasPreviousPage: false,
fetchNextPage: jest.fn(),
fetchPreviousPage: jest.fn(),
isFetchingNextPage: false,
isFetchingPreviousPage: false,
dataUpdatedAt: Date.now(),
errorUpdatedAt: 0,
failureCount: 0,
failureReason: null,
isFetched: true,
isFetchedAfterMount: true,
isFetching: false,
isInitialLoading: false,
isLoadingError: false,
isPaused: false,
isPlaceholderData: false,
isPreviousData: false,
isRefetchError: false,
isRefetching: false,
isStale: false,
promise: Promise.resolve({
pages: [['item1'], ['item2']],
pageParams: [undefined, 'next-page'],
}),
});

describe('plain data source', () => {
const plainDataSource: AnyPlainQueryDataSource = {
type: 'plain',
name: 'test-plain',
fetch: jest.fn().mockResolvedValue({data: 'test-data'}),
};

it('should use original refetch when no enabled option', () => {
const originalRefetch = jest.fn();
mockUseQuery.mockReturnValue(createMockQueryResult(originalRefetch) as any);

Check warning on line 109 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() => useQueryData(plainDataSource, {id: 1}));

expect(result.current.refetch).toBe(originalRefetch);
expect(result.current.refetch).not.toBe(mockWarnDisabledRefetch);
});

it('should use warnDisabledRefetch when enabled: false', () => {
const originalRefetch = jest.fn();
mockUseQuery.mockReturnValue(createMockQueryResult(originalRefetch) as any);

Check warning on line 119 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() =>
useQueryData(plainDataSource, {id: 1}, {enabled: false}),
);

expect(result.current.refetch).toBe(mockWarnDisabledRefetch);
expect(result.current.refetch).not.toBe(originalRefetch);
});

it('should use warnDisabledRefetch when params is idle', () => {
const originalRefetch = jest.fn();
mockUseQuery.mockReturnValue(createMockQueryResult(originalRefetch) as any);

Check warning on line 131 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() => useQueryData(plainDataSource, idle));

expect(result.current.refetch).toBe(mockWarnDisabledRefetch);
expect(result.current.refetch).not.toBe(originalRefetch);
});
});

describe('infinite data source', () => {
const infiniteDataSource: AnyInfiniteQueryDataSource = {
type: 'infinite',
name: 'test-infinite',
fetch: jest.fn().mockResolvedValue({data: ['item1', 'item2']}),
next: jest.fn(),
};

it('should use original refetch when no enabled option', () => {
const originalRefetch = jest.fn();
mockUseInfiniteQuery.mockReturnValue(createMockInfiniteResult(originalRefetch) as any);

Check warning on line 150 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() => useQueryData(infiniteDataSource, {id: 1}));

expect(result.current.refetch).toBe(originalRefetch);
expect(result.current.refetch).not.toBe(mockWarnDisabledRefetch);
});

it('should use warnDisabledRefetch when enabled: false', () => {
const originalRefetch = jest.fn();
mockUseInfiniteQuery.mockReturnValue(createMockInfiniteResult(originalRefetch) as any);

Check warning on line 160 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() =>
useQueryData(infiniteDataSource, {id: 1}, {enabled: false}),
);

expect(result.current.refetch).toBe(mockWarnDisabledRefetch);
expect(result.current.refetch).not.toBe(originalRefetch);
});

it('should use warnDisabledRefetch when params is idle', () => {
const originalRefetch = jest.fn();
mockUseInfiniteQuery.mockReturnValue(createMockInfiniteResult(originalRefetch) as any);

Check warning on line 172 in src/react-query/hooks/__tests__/useQueryData.refetch.test.ts

View workflow job for this annotation

GitHub Actions / Lint & Typecheck

Unexpected any. Specify a different type

const {result} = renderHook(() => useQueryData(infiniteDataSource, idle));

expect(result.current.refetch).toBe(mockWarnDisabledRefetch);
expect(result.current.refetch).not.toBe(originalRefetch);
});
});
});
6 changes: 5 additions & 1 deletion src/react-query/impl/infinite/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useMemo} from 'react';

import {useInfiniteQuery} from '@tanstack/react-query';
import {skipToken, useInfiniteQuery} from '@tanstack/react-query';
import type {InfiniteData, InfiniteQueryObserverOptions} from '@tanstack/react-query';

import type {
Expand All @@ -16,6 +16,7 @@ import type {
} from '../../../core';
import {useRefetchInterval} from '../../hooks/useRefetchInterval';
import {normalizeStatus} from '../../utils/normalizeStatus';
import {warnDisabledRefetch} from '../../utils/warnDisabledRefetch';

import type {AnyInfiniteQueryDataSource, InfiniteQueryObserverExtendedOptions} from './types';
import {composeOptions} from './utils';
Expand Down Expand Up @@ -63,11 +64,14 @@ export const useInfiniteQueryData = <TDataSource extends AnyInfiniteQueryDataSou
[state.data],
);

const isDisabled = composedOptions.enabled === false || composedOptions.queryFn === skipToken;

return {
...state,
status: normalizeStatus(state.status, state.fetchStatus),
data: transformedData,
originalStatus: state.status,
originalData: state.data,
refetch: isDisabled ? warnDisabledRefetch : state.refetch,
} as DataSourceState<TDataSource>;
};
6 changes: 5 additions & 1 deletion src/react-query/impl/plain/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {type QueryObserverOptions, useQuery} from '@tanstack/react-query';
import {type QueryObserverOptions, skipToken, useQuery} from '@tanstack/react-query';

import type {
DataSourceContext,
Expand All @@ -12,6 +12,7 @@ import type {
} from '../../../core';
import {useRefetchInterval} from '../../hooks/useRefetchInterval';
import {normalizeStatus} from '../../utils/normalizeStatus';
import {warnDisabledRefetch} from '../../utils/warnDisabledRefetch';

import type {AnyPlainQueryDataSource, QueryObserverExtendedOptions} from './types';
import {composeOptions} from './utils';
Expand Down Expand Up @@ -52,9 +53,12 @@ export const usePlainQueryData = <TDataSource extends AnyPlainQueryDataSource>(
const composedOptions = usePlainQueryDataOptions(extendedOptions);
const state = useQuery(composedOptions);

const isDisabled = composedOptions.enabled === false || composedOptions.queryFn === skipToken;

return {
...state,
status: normalizeStatus(state.status, state.fetchStatus),
originalStatus: state.status,
refetch: isDisabled ? warnDisabledRefetch : state.refetch,
} as DataSourceState<TDataSource>;
};
5 changes: 5 additions & 0 deletions src/react-query/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ export interface QueryDataAdditionalOptions<
TQueryKey extends QueryKey = QueryKey,
> {
refetchInterval?: RefetchInterval<TQueryFnData, TError, TQueryData, TQueryKey>;
/**
* @deprecated The use of the enabled option is deprecated.
* It is recommended to use idle as query parameters to control query state.
*/
enabled?: boolean;
}
8 changes: 8 additions & 0 deletions src/react-query/utils/warn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function warn(msg: string) {
if (!msg || process.env.NODE_ENV === 'production') {
return;
}

// eslint-disable-next-line no-console
console.warn(msg);
}
5 changes: 5 additions & 0 deletions src/react-query/utils/warnDisabledRefetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {warn} from './warn';

export const warnDisabledRefetch = () => {
warn('Disabled refetch is called');
};