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
20 changes: 18 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
"@babel/preset-typescript",
"@babel/react"
],
"plugins": ["babel-plugin-transform-async-to-promises"],
"plugins": [
[
"const-enum",
{
"transform": "constObject"
}
],
"babel-plugin-transform-async-to-promises"
],
"env": {
"test": {
"presets": [
Expand All @@ -24,7 +32,15 @@
}
]
],
"plugins": ["babel-plugin-transform-async-to-promises"]
"plugins": [
[
"const-enum",
{
"transform": "constObject"
}
],
"babel-plugin-transform-async-to-promises"
]
}
}
}
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
{
"ignoreParameters": true
}
]
],
"no-shadow": "error"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@typescript-eslint/parser": "^3.6.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.0.1",
"babel-plugin-const-enum": "^1.0.1",
"babel-plugin-transform-async-to-promises": "^0.8.15",
"cross-env": "^7.0.2",
"eslint": "7.x",
Expand Down
59 changes: 27 additions & 32 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isDocumentVisible,
isOnline,
isServer,
isValidTimeout,
noop,
replaceEqualDeep,
sleep,
Expand All @@ -26,14 +27,6 @@ import { QueryObserver, UpdateListener } from './queryObserver'

// TYPES

interface QueryInitConfig<TResult, TError> {
queryCache: QueryCache
queryKey: ArrayQueryKey
queryHash: string
config: QueryConfig<TResult, TError>
notifyGlobalListeners: (query: Query<TResult, TError>) => void
}

export interface QueryState<TResult, TError> {
canFetchMore?: boolean
data?: TResult
Expand Down Expand Up @@ -65,11 +58,11 @@ export interface RefetchOptions {
throwOnError?: boolean
}

export enum ActionType {
Failed = 'Failed',
Fetch = 'Fetch',
Success = 'Success',
Error = 'Error',
const enum ActionType {
Failed,
Fetch,
Success,
Error,
}

interface FailedAction {
Expand Down Expand Up @@ -114,17 +107,19 @@ export class Query<TResult, TError> {
private cancelFetch?: () => void
private continueFetch?: () => void
private isTransportCancelable?: boolean
private notifyGlobalListeners: (query: Query<TResult, TError>) => void

constructor(init: QueryInitConfig<TResult, TError>) {
this.config = init.config
this.queryCache = init.queryCache
this.queryKey = init.queryKey
this.queryHash = init.queryHash
this.notifyGlobalListeners = init.notifyGlobalListeners

constructor(
queryKey: ArrayQueryKey,
queryHash: string,
config: QueryConfig<TResult, TError>
) {
this.config = config
this.queryKey = queryKey
this.queryHash = queryHash
this.queryCache = config.queryCache!
this.observers = []
this.state = getDefaultState(init.config)
this.cacheTime = init.config.cacheTime!
this.state = getDefaultState(config)
this.cacheTime = config.cacheTime!
this.scheduleGc()
}

Expand All @@ -140,7 +135,7 @@ export class Query<TResult, TError> {
observer.onQueryUpdate(action)
})

this.notifyGlobalListeners(this)
this.queryCache.notifyGlobalListeners(this)
}

private scheduleGc(): void {
Expand All @@ -150,7 +145,7 @@ export class Query<TResult, TError> {

this.clearGcTimeout()

if (this.cacheTime === Infinity || this.observers.length > 0) {
if (this.observers.length > 0 || !isValidTimeout(this.cacheTime)) {
return
}

Expand Down Expand Up @@ -222,7 +217,7 @@ export class Query<TResult, TError> {
}

isStale(): boolean {
return this.observers.some(observer => observer.isStale())
return this.observers.some(observer => observer.getCurrentResult().isStale)
}

isStaleByTime(staleTime = 0): boolean {
Expand All @@ -234,16 +229,16 @@ export class Query<TResult, TError> {
onInteraction(type: 'focus' | 'online'): void {
// Execute the first observer which is enabled,
// stale and wants to refetch on this interaction.
const observer = this.observers.find(
const staleObserver = this.observers.find(
observer =>
observer.isStale() &&
observer.getCurrentResult().isStale &&
observer.config.enabled &&
((observer.config.refetchOnWindowFocus && type === 'focus') ||
(observer.config.refetchOnReconnect && type === 'online'))
)

if (observer) {
observer.fetch().catch(noop)
if (staleObserver) {
staleObserver.fetch().catch(noop)
}

// Continue any paused fetch
Expand Down Expand Up @@ -280,9 +275,9 @@ export class Query<TResult, TError> {
if (this.isTransportCancelable) {
this.cancel()
}
}

this.scheduleGc()
this.scheduleGc()
}
}

async refetch(
Expand Down
28 changes: 10 additions & 18 deletions src/core/queryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
functionalUpdate,
getQueryArgs,
isDocumentVisible,
isObject,
isPlainObject,
isOnline,
isServer,
} from './utils'
Expand Down Expand Up @@ -77,18 +77,15 @@ export class QueryCache {

constructor(config?: QueryCacheConfig) {
this.config = config || {}

// A frozen cache does not add new queries to the cache
this.globalListeners = []

this.queries = {}
this.queriesArray = []
this.isFetching = 0
}

private notifyGlobalListeners(query?: Query<any, any>) {
notifyGlobalListeners(query?: Query<any, any>) {
this.isFetching = this.getQueries().reduce(
(acc, query) => (query.state.isFetching ? acc + 1 : acc),
(acc, q) => (q.state.isFetching ? acc + 1 : acc),
0
)

Expand Down Expand Up @@ -228,16 +225,9 @@ export class QueryCache {
return this.queries[queryHash] as Query<TResult, TError>
}

const query = new Query<TResult, TError>({
queryCache: this,
queryKey,
queryHash,
config,
notifyGlobalListeners: query => {
this.notifyGlobalListeners(query)
},
})
const query = new Query<TResult, TError>(queryKey, queryHash, config)

// A frozen cache does not add new queries to the cache
if (!this.config.frozen) {
this.queries[queryHash] = query
this.queriesArray.push(query)
Expand Down Expand Up @@ -291,7 +281,7 @@ export class QueryCache {
...args: any[]
): Promise<TResult | undefined> {
if (
isObject(args[1]) &&
isPlainObject(args[1]) &&
(args[1].hasOwnProperty('throwOnError') ||
args[1].hasOwnProperty('force'))
) {
Expand All @@ -312,9 +302,11 @@ export class QueryCache {
...config,
})

let query
try {
query = this.buildQuery<TResult, TError>(queryKey, configWithoutRetry)
const query = this.buildQuery<TResult, TError>(
queryKey,
configWithoutRetry
)
if (options?.force || query.isStaleByTime(config.staleTime)) {
await query.fetch(undefined, configWithoutRetry)
}
Expand Down
50 changes: 19 additions & 31 deletions src/core/queryObserver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { getStatusProps, isServer, isDocumentVisible } from './utils'
import {
getStatusProps,
isServer,
isDocumentVisible,
isValidTimeout,
} from './utils'
import type { QueryResult, QueryObserverConfig } from './types'
import type { Query, Action, FetchMoreOptions, RefetchOptions } from './query'
import type { QueryCache } from './queryCache'
Expand Down Expand Up @@ -90,18 +95,12 @@ export class QueryObserver<TResult, TError> {
// Update refetch interval if needed
if (
config.enabled !== prevConfig.enabled ||
config.refetchInterval !== prevConfig.refetchInterval ||
config.refetchIntervalInBackground !==
prevConfig.refetchIntervalInBackground
config.refetchInterval !== prevConfig.refetchInterval
) {
this.updateRefetchInterval()
}
}

isStale(): boolean {
return this.currentResult.isStale
}

getCurrentQuery(): Query<TResult, TError> {
return this.currentQuery
}
Expand Down Expand Up @@ -144,14 +143,6 @@ export class QueryObserver<TResult, TError> {
}
}

private updateIsStale(): void {
const isStale = this.currentQuery.isStaleByTime(this.config.staleTime)
if (isStale !== this.currentResult.isStale) {
this.updateResult()
this.notify()
}
}

private notify(): void {
this.updateListener?.(this.currentResult)
}
Expand All @@ -163,19 +154,19 @@ export class QueryObserver<TResult, TError> {

this.clearStaleTimeout()

const staleTime = this.config.staleTime || 0
const { isStale, updatedAt } = this.currentResult

if (isStale || staleTime === Infinity) {
if (this.currentResult.isStale || !isValidTimeout(this.config.staleTime)) {
return
}

const timeElapsed = Date.now() - updatedAt
const timeUntilStale = staleTime - timeElapsed + 1
const timeElapsed = Date.now() - this.currentResult.updatedAt
const timeUntilStale = this.config.staleTime - timeElapsed + 1
const timeout = Math.max(timeUntilStale, 0)

this.staleTimeoutId = setTimeout(() => {
this.updateIsStale()
if (!this.currentResult.isStale) {
this.currentResult = { ...this.currentResult, isStale: true }
this.notify()
}
}, timeout)
}

Expand All @@ -186,12 +177,7 @@ export class QueryObserver<TResult, TError> {

this.clearRefetchInterval()

if (
!this.config.enabled ||
!this.config.refetchInterval ||
this.config.refetchInterval < 0 ||
this.config.refetchInterval === Infinity
) {
if (!this.config.enabled || !isValidTimeout(this.config.refetchInterval)) {
return
}

Expand Down Expand Up @@ -309,6 +295,8 @@ export class QueryObserver<TResult, TError> {
}

onQueryUpdate(action: Action<TResult, TError>): void {
const { type } = action

// Store current result and get new result
const prevResult = this.currentResult
this.updateResult()
Expand All @@ -317,11 +305,11 @@ export class QueryObserver<TResult, TError> {

// We need to check the action because the state could have
// transitioned from success to success in case of `setQueryData`.
if (action.type === 'Success' && currentResult.isSuccess) {
if (type === 2) {
config.onSuccess?.(currentResult.data!)
config.onSettled?.(currentResult.data!, null)
this.updateTimers()
} else if (action.type === 'Error' && currentResult.isError) {
} else if (type === 3) {
config.onError?.(currentResult.error!)
config.onSettled?.(undefined, currentResult.error!)
this.updateTimers()
Expand Down
Loading