Skip to content

Commit

Permalink
refactor: cleanup to reduce file size and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
boschni committed Sep 6, 2020
1 parent cb2b425 commit 6d46633
Show file tree
Hide file tree
Showing 20 changed files with 324 additions and 206 deletions.
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

0 comments on commit 6d46633

Please sign in to comment.