Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0d28e0a
refactor(core): add generic utilities for resolving value-or-function…
braden-w May 29, 2025
cd59cc4
refactor(types): simplify placeholderData type definition by removing…
braden-w May 29, 2025
427c977
Merge branch 'upstream/main' into refactor/resolveValueOrFunction
braden-w May 29, 2025
c03881a
chore(query): replace resolveStaleTime with resolveValueOrFunction
braden-w May 29, 2025
44416bf
chore(deps): add tsup dependency and fix build
braden-w May 29, 2025
af723ec
Merge branch 'main' into refactor/resolveValueOrFunction
TkDodo Jun 2, 2025
63730f0
Merge upstream/main into refactor/resolveValueOrFunction
braden-w Jul 9, 2025
d486197
Merge remote-tracking branch 'origin/main' into refactor/resolveValue…
braden-w Aug 11, 2025
f940937
refactor: rename resolveValueOrFunction to resolveOption for consistency
braden-w Aug 11, 2025
a693166
ci: apply automated fixes
autofix-ci[bot] Aug 11, 2025
24d06b5
refactor(queryClient): update method call to use resolveOption for im…
braden-w Aug 11, 2025
568d2d0
refactor: replace resolveEnabled with resolveOption for consistency
braden-w Aug 11, 2025
df4850a
ci: apply automated fixes
autofix-ci[bot] Aug 11, 2025
d6ea2f1
chore: remove unused dependency tsup from package.json
braden-w Aug 19, 2025
6fff343
refactor: simplify functionalUpdate by using resolveOption
braden-w Aug 19, 2025
41b79fd
Merge remote-tracking branch 'upstream/main' into refactor/resolveVal…
braden-w Aug 19, 2025
8854b1a
fix: correct spelling of 'retryer' in comment
braden-w Aug 20, 2025
77bab27
chore: pnpm lock
braden-w Aug 21, 2025
ae7a70b
Merge branch 'main' into refactor/resolveValueOrFunction
TkDodo Sep 1, 2025
8955b0d
refactor(core): constrain resolveOption to non-function types
braden-w Sep 16, 2025
c64a5ea
Merge branch 'main' of https://github.com/TanStack/query into refacto…
braden-w Sep 16, 2025
92a0b66
docs(core): improve NonFunction type organization and documentation
braden-w Sep 17, 2025
aaaaf3c
refactor(core): improve resolveOption function with better type safety
braden-w Sep 17, 2025
87398fd
refactor(query-core): replace functionalUpdate with resolveOption in …
braden-w Sep 17, 2025
251bf4a
docs(core): enhance resolveOption function documentation
braden-w Sep 17, 2025
ecd18db
ci: apply automated fixes
autofix-ci[bot] Sep 23, 2025
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
19 changes: 6 additions & 13 deletions packages/query-core/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import {
ensureQueryFn,
noop,
replaceData,
resolveEnabled,
resolveStaleTime,
resolveOption,
skipToken,
timeUntilStale,
} from './utils'
Expand All @@ -16,7 +15,6 @@ import type {
CancelOptions,
DefaultError,
FetchStatus,
InitialDataFunction,
OmitKeyof,
QueryFunctionContext,
QueryKey,
Expand Down Expand Up @@ -268,7 +266,7 @@ export class Query<

isActive(): boolean {
return this.observers.some(
(observer) => resolveEnabled(observer.options.enabled, this) !== false,
(observer) => resolveOption(observer.options.enabled, this) !== false,
)
}

Expand All @@ -287,7 +285,7 @@ export class Query<
if (this.getObserversCount() > 0) {
return this.observers.some(
(observer) =>
resolveStaleTime(observer.options.staleTime, this) === 'static',
resolveOption(observer.options.staleTime, this) === 'static',
)
}

Expand Down Expand Up @@ -390,7 +388,7 @@ export class Query<
): Promise<TData> {
if (
this.state.fetchStatus !== 'idle' &&
// If the promise in the retyer is already rejected, we have to definitely
// If the promise in the retryer is already rejected, we have to definitely
// re-start the fetch; there is a chance that the query is still in a
// pending state when that happens
this.#retryer?.status() !== 'rejected'
Expand Down Expand Up @@ -718,17 +716,12 @@ function getDefaultState<
>(
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): QueryState<TData, TError> {
const data =
typeof options.initialData === 'function'
? (options.initialData as InitialDataFunction<TData>)()
: options.initialData
const data = resolveOption(options.initialData)

const hasData = data !== undefined

const initialDataUpdatedAt = hasData
? typeof options.initialDataUpdatedAt === 'function'
? (options.initialDataUpdatedAt as () => number | undefined)()
: options.initialDataUpdatedAt
? resolveOption(options.initialDataUpdatedAt)
: 0

return {
Expand Down
11 changes: 4 additions & 7 deletions packages/query-core/src/queryClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
functionalUpdate,
hashKey,
hashQueryKeyByOptions,
noop,
partialMatchKey,
resolveStaleTime,
resolveOption,
skipToken,
} from './utils'
import { QueryCache } from './queryCache'
Expand Down Expand Up @@ -155,7 +154,7 @@ export class QueryClient {

if (
options.revalidateIfStale &&
query.isStaleByTime(resolveStaleTime(defaultedOptions.staleTime, query))
query.isStaleByTime(resolveOption(defaultedOptions.staleTime, query))
) {
void this.prefetchQuery(defaultedOptions)
}
Expand Down Expand Up @@ -197,7 +196,7 @@ export class QueryClient {
defaultedOptions.queryHash,
)
const prevData = query?.state.data
const data = functionalUpdate(updater, prevData)
const data = resolveOption(updater, prevData)

if (data === undefined) {
return undefined
Expand Down Expand Up @@ -362,9 +361,7 @@ export class QueryClient {

const query = this.#queryCache.build(this, defaultedOptions)

return query.isStaleByTime(
resolveStaleTime(defaultedOptions.staleTime, query),
)
return query.isStaleByTime(resolveOption(defaultedOptions.staleTime, query))
? query.fetch(defaultedOptions)
: Promise.resolve(query.state.data as TData)
}
Expand Down
64 changes: 25 additions & 39 deletions packages/query-core/src/queryObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
isValidTimeout,
noop,
replaceData,
resolveEnabled,
resolveStaleTime,
resolveOption,
shallowEqualObjects,
timeUntilStale,
} from './utils'
Expand All @@ -21,7 +20,6 @@ import type { PendingThenable, Thenable } from './thenable'
import type {
DefaultError,
DefaultedQueryObserverOptions,
PlaceholderDataFunction,
QueryKey,
QueryObserverBaseResult,
QueryObserverOptions,
Expand Down Expand Up @@ -154,7 +152,7 @@ export class QueryObserver<
this.options.enabled !== undefined &&
typeof this.options.enabled !== 'boolean' &&
typeof this.options.enabled !== 'function' &&
typeof resolveEnabled(this.options.enabled, this.#currentQuery) !==
typeof resolveOption(this.options.enabled, this.#currentQuery) !==
'boolean'
) {
throw new Error(
Expand Down Expand Up @@ -198,10 +196,10 @@ export class QueryObserver<
if (
mounted &&
(this.#currentQuery !== prevQuery ||
resolveEnabled(this.options.enabled, this.#currentQuery) !==
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
resolveStaleTime(this.options.staleTime, this.#currentQuery) !==
resolveStaleTime(prevOptions.staleTime, this.#currentQuery))
resolveOption(this.options.enabled, this.#currentQuery) !==
resolveOption(prevOptions.enabled, this.#currentQuery) ||
resolveOption(this.options.staleTime, this.#currentQuery) !==
resolveOption(prevOptions.staleTime, this.#currentQuery))
) {
this.#updateStaleTimeout()
}
Expand All @@ -212,8 +210,8 @@ export class QueryObserver<
if (
mounted &&
(this.#currentQuery !== prevQuery ||
resolveEnabled(this.options.enabled, this.#currentQuery) !==
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
resolveOption(this.options.enabled, this.#currentQuery) !==
resolveOption(prevOptions.enabled, this.#currentQuery) ||
nextRefetchInterval !== this.#currentRefetchInterval)
) {
this.#updateRefetchInterval(nextRefetchInterval)
Expand Down Expand Up @@ -352,10 +350,7 @@ export class QueryObserver<

#updateStaleTimeout(): void {
this.#clearStaleTimeout()
const staleTime = resolveStaleTime(
this.options.staleTime,
this.#currentQuery,
)
const staleTime = resolveOption(this.options.staleTime, this.#currentQuery)

if (isServer || this.#currentResult.isStale || !isValidTimeout(staleTime)) {
return
Expand All @@ -376,9 +371,7 @@ export class QueryObserver<

#computeRefetchInterval() {
return (
(typeof this.options.refetchInterval === 'function'
? this.options.refetchInterval(this.#currentQuery)
: this.options.refetchInterval) ?? false
resolveOption(this.options.refetchInterval, this.#currentQuery) ?? false
)
}

Expand All @@ -389,7 +382,7 @@ export class QueryObserver<

if (
isServer ||
resolveEnabled(this.options.enabled, this.#currentQuery) === false ||
resolveOption(this.options.enabled, this.#currentQuery) === false ||
!isValidTimeout(this.#currentRefetchInterval) ||
this.#currentRefetchInterval === 0
) {
Expand Down Expand Up @@ -497,15 +490,11 @@ export class QueryObserver<
skipSelect = true
} else {
// compute placeholderData
placeholderData =
typeof options.placeholderData === 'function'
? (
options.placeholderData as unknown as PlaceholderDataFunction<TQueryData>
)(
this.#lastQueryWithDefinedData?.state.data,
this.#lastQueryWithDefinedData as any,
)
: options.placeholderData
placeholderData = resolveOption(
options.placeholderData,
this.#lastQueryWithDefinedData?.state.data,
this.#lastQueryWithDefinedData as any,
)
}

if (placeholderData !== undefined) {
Expand Down Expand Up @@ -584,7 +573,7 @@ export class QueryObserver<
isStale: isStale(query, options),
refetch: this.refetch,
promise: this.#currentThenable,
isEnabled: resolveEnabled(options.enabled, query) !== false,
isEnabled: resolveOption(options.enabled, query) !== false,
}

const nextResult = result as QueryObserverResult<TData, TError>
Expand Down Expand Up @@ -668,10 +657,7 @@ export class QueryObserver<
}

const { notifyOnChangeProps } = this.options
const notifyOnChangePropsValue =
typeof notifyOnChangeProps === 'function'
? notifyOnChangeProps()
: notifyOnChangeProps
const notifyOnChangePropsValue = resolveOption(notifyOnChangeProps)

if (
notifyOnChangePropsValue === 'all' ||
Expand Down Expand Up @@ -749,7 +735,7 @@ function shouldLoadOnMount(
options: QueryObserverOptions<any, any, any, any>,
): boolean {
return (
resolveEnabled(options.enabled, query) !== false &&
resolveOption(options.enabled, query) !== false &&
query.state.data === undefined &&
!(query.state.status === 'error' && options.retryOnMount === false)
)
Expand All @@ -774,10 +760,10 @@ function shouldFetchOn(
(typeof options)['refetchOnReconnect'],
) {
if (
resolveEnabled(options.enabled, query) !== false &&
resolveStaleTime(options.staleTime, query) !== 'static'
resolveOption(options.enabled, query) !== false &&
resolveOption(options.staleTime, query) !== 'static'
) {
const value = typeof field === 'function' ? field(query) : field
const value = resolveOption(field, query)

return value === 'always' || (value !== false && isStale(query, options))
}
Expand All @@ -792,7 +778,7 @@ function shouldFetchOptionally(
): boolean {
return (
(query !== prevQuery ||
resolveEnabled(prevOptions.enabled, query) === false) &&
resolveOption(prevOptions.enabled, query) === false) &&
(!options.suspense || query.state.status !== 'error') &&
isStale(query, options)
)
Expand All @@ -803,8 +789,8 @@ function isStale(
options: QueryObserverOptions<any, any, any, any, any>,
): boolean {
return (
resolveEnabled(options.enabled, query) !== false &&
query.isStaleByTime(resolveStaleTime(options.staleTime, query))
resolveOption(options.enabled, query) !== false &&
query.isStaleByTime(resolveOption(options.staleTime, query))
)
}

Expand Down
7 changes: 2 additions & 5 deletions packages/query-core/src/retryer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { focusManager } from './focusManager'
import { onlineManager } from './onlineManager'
import { pendingThenable } from './thenable'
import { isServer, sleep } from './utils'
import { isServer, resolveOption, sleep } from './utils'
import type { Thenable } from './thenable'
import type { CancelOptions, DefaultError, NetworkMode } from './types'

Expand Down Expand Up @@ -168,10 +168,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
// Do we need to retry the request?
const retry = config.retry ?? (isServer ? 0 : 3)
const retryDelay = config.retryDelay ?? defaultRetryDelay
const delay =
typeof retryDelay === 'function'
? retryDelay(failureCount, error)
: retryDelay
const delay = resolveOption(retryDelay, failureCount, error)
const shouldRetry =
retry === true ||
(typeof retry === 'number' && failureCount < retry) ||
Expand Down
13 changes: 3 additions & 10 deletions packages/query-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,7 @@ export type QueryFunctionContext<

export type InitialDataFunction<T> = () => T | undefined

type NonFunctionGuard<T> = T extends Function ? never : T

export type PlaceholderDataFunction<
type PlaceholderDataFunction<
TQueryFnData = unknown,
TError = DefaultError,
TQueryData = TQueryFnData,
Expand Down Expand Up @@ -424,13 +422,8 @@ export interface QueryObserverOptions<
* If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided.
*/
placeholderData?:
| NonFunctionGuard<TQueryData>
| PlaceholderDataFunction<
NonFunctionGuard<TQueryData>,
TError,
NonFunctionGuard<TQueryData>,
TQueryKey
>
| TQueryData
| PlaceholderDataFunction<TQueryData, TError, TQueryData, TQueryKey>

_optimisticResults?: 'optimistic' | 'isRestoring'

Expand Down
Loading
Loading