Skip to content

Commit 27e39bb

Browse files
committed
refactor: cleanup to reduce file size and add tests
1 parent 1117a51 commit 27e39bb

20 files changed

+289
-175
lines changed

.babelrc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@
1111
"@babel/preset-typescript",
1212
"@babel/react"
1313
],
14-
"plugins": ["babel-plugin-transform-async-to-promises"],
14+
"plugins": [
15+
[
16+
"const-enum",
17+
{
18+
"transform": "constObject"
19+
}
20+
],
21+
"babel-plugin-transform-async-to-promises"
22+
],
1523
"env": {
1624
"test": {
1725
"presets": [
@@ -24,7 +32,15 @@
2432
}
2533
]
2634
],
27-
"plugins": ["babel-plugin-transform-async-to-promises"]
35+
"plugins": [
36+
[
37+
"const-enum",
38+
{
39+
"transform": "constObject"
40+
}
41+
],
42+
"babel-plugin-transform-async-to-promises"
43+
]
2844
}
2945
}
3046
}

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
{
2626
"ignoreParameters": true
2727
}
28-
]
28+
],
29+
"no-shadow": "error"
2930
}
3031
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@typescript-eslint/parser": "^3.6.1",
6464
"babel-eslint": "^10.1.0",
6565
"babel-jest": "^26.0.1",
66+
"babel-plugin-const-enum": "^1.0.1",
6667
"babel-plugin-transform-async-to-promises": "^0.8.15",
6768
"cross-env": "^7.0.2",
6869
"eslint": "7.x",

src/core/query.ts

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
isDocumentVisible,
1010
isOnline,
1111
isServer,
12+
isValidTimeout,
1213
noop,
1314
replaceEqualDeep,
1415
sleep,
@@ -26,14 +27,6 @@ import { QueryObserver, UpdateListener } from './queryObserver'
2627

2728
// TYPES
2829

29-
interface QueryInitConfig<TResult, TError> {
30-
queryCache: QueryCache
31-
queryKey: ArrayQueryKey
32-
queryHash: string
33-
config: QueryConfig<TResult, TError>
34-
notifyGlobalListeners: (query: Query<TResult, TError>) => void
35-
}
36-
3730
export interface QueryState<TResult, TError> {
3831
canFetchMore?: boolean
3932
data?: TResult
@@ -65,11 +58,11 @@ export interface RefetchOptions {
6558
throwOnError?: boolean
6659
}
6760

68-
export enum ActionType {
69-
Failed = 'Failed',
70-
Fetch = 'Fetch',
71-
Success = 'Success',
72-
Error = 'Error',
61+
const enum ActionType {
62+
Failed,
63+
Fetch,
64+
Success,
65+
Error,
7366
}
7467

7568
interface FailedAction {
@@ -114,17 +107,19 @@ export class Query<TResult, TError> {
114107
private cancelFetch?: () => void
115108
private continueFetch?: () => void
116109
private isTransportCancelable?: boolean
117-
private notifyGlobalListeners: (query: Query<TResult, TError>) => void
118-
119-
constructor(init: QueryInitConfig<TResult, TError>) {
120-
this.config = init.config
121-
this.queryCache = init.queryCache
122-
this.queryKey = init.queryKey
123-
this.queryHash = init.queryHash
124-
this.notifyGlobalListeners = init.notifyGlobalListeners
110+
111+
constructor(
112+
queryKey: ArrayQueryKey,
113+
queryHash: string,
114+
config: QueryConfig<TResult, TError>
115+
) {
116+
this.config = config
117+
this.queryKey = queryKey
118+
this.queryHash = queryHash
119+
this.queryCache = config.queryCache!
125120
this.observers = []
126-
this.state = getDefaultState(init.config)
127-
this.cacheTime = init.config.cacheTime!
121+
this.state = getDefaultState(config)
122+
this.cacheTime = config.cacheTime!
128123
this.scheduleGc()
129124
}
130125

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

143-
this.notifyGlobalListeners(this)
138+
this.queryCache.notifyGlobalListeners(this)
144139
}
145140

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

151146
this.clearGcTimeout()
152147

153-
if (this.cacheTime === Infinity || this.observers.length > 0) {
148+
if (this.observers.length > 0 || !isValidTimeout(this.cacheTime)) {
154149
return
155150
}
156151

@@ -234,16 +229,16 @@ export class Query<TResult, TError> {
234229
onInteraction(type: 'focus' | 'online'): void {
235230
// Execute the first observer which is enabled,
236231
// stale and wants to refetch on this interaction.
237-
const observer = this.observers.find(
232+
const staleObserver = this.observers.find(
238233
observer =>
239234
observer.isStale() &&
240235
observer.config.enabled &&
241236
((observer.config.refetchOnWindowFocus && type === 'focus') ||
242237
(observer.config.refetchOnReconnect && type === 'online'))
243238
)
244239

245-
if (observer) {
246-
observer.fetch().catch(noop)
240+
if (staleObserver) {
241+
staleObserver.fetch().catch(noop)
247242
}
248243

249244
// Continue any paused fetch
@@ -280,9 +275,9 @@ export class Query<TResult, TError> {
280275
if (this.isTransportCancelable) {
281276
this.cancel()
282277
}
283-
}
284278

285-
this.scheduleGc()
279+
this.scheduleGc()
280+
}
286281
}
287282

288283
async refetch(

src/core/queryCache.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,13 @@ export class QueryCache {
8484
this.queries = {}
8585
this.queriesArray = []
8686
this.isFetching = 0
87+
88+
this.notifyGlobalListeners = this.notifyGlobalListeners.bind(this)
8789
}
8890

89-
private notifyGlobalListeners(query?: Query<any, any>) {
91+
notifyGlobalListeners(query?: Query<any, any>) {
9092
this.isFetching = this.getQueries().reduce(
91-
(acc, query) => (query.state.isFetching ? acc + 1 : acc),
93+
(acc, q) => (q.state.isFetching ? acc + 1 : acc),
9294
0
9395
)
9496

@@ -228,15 +230,7 @@ export class QueryCache {
228230
return this.queries[queryHash] as Query<TResult, TError>
229231
}
230232

231-
const query = new Query<TResult, TError>({
232-
queryCache: this,
233-
queryKey,
234-
queryHash,
235-
config,
236-
notifyGlobalListeners: query => {
237-
this.notifyGlobalListeners(query)
238-
},
239-
})
233+
const query = new Query<TResult, TError>(queryKey, queryHash, config)
240234

241235
if (!this.config.frozen) {
242236
this.queries[queryHash] = query
@@ -312,9 +306,11 @@ export class QueryCache {
312306
...config,
313307
})
314308

315-
let query
316309
try {
317-
query = this.buildQuery<TResult, TError>(queryKey, configWithoutRetry)
310+
const query = this.buildQuery<TResult, TError>(
311+
queryKey,
312+
configWithoutRetry
313+
)
318314
if (options?.force || query.isStaleByTime(config.staleTime)) {
319315
await query.fetch(undefined, configWithoutRetry)
320316
}

src/core/queryObserver.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { getStatusProps, isServer, isDocumentVisible } from './utils'
1+
import {
2+
getStatusProps,
3+
isServer,
4+
isDocumentVisible,
5+
isValidTimeout,
6+
} from './utils'
27
import type { QueryResult, QueryObserverConfig } from './types'
38
import type { Query, Action, FetchMoreOptions, RefetchOptions } from './query'
49
import type { QueryCache } from './queryCache'
@@ -90,9 +95,7 @@ export class QueryObserver<TResult, TError> {
9095
// Update refetch interval if needed
9196
if (
9297
config.enabled !== prevConfig.enabled ||
93-
config.refetchInterval !== prevConfig.refetchInterval ||
94-
config.refetchIntervalInBackground !==
95-
prevConfig.refetchIntervalInBackground
98+
config.refetchInterval !== prevConfig.refetchInterval
9699
) {
97100
this.updateRefetchInterval()
98101
}
@@ -163,15 +166,14 @@ export class QueryObserver<TResult, TError> {
163166

164167
this.clearStaleTimeout()
165168

166-
const staleTime = this.config.staleTime || 0
167169
const { isStale, updatedAt } = this.currentResult
168170

169-
if (isStale || staleTime === Infinity) {
171+
if (isStale || !isValidTimeout(this.config.staleTime)) {
170172
return
171173
}
172174

173175
const timeElapsed = Date.now() - updatedAt
174-
const timeUntilStale = staleTime - timeElapsed
176+
const timeUntilStale = this.config.staleTime - timeElapsed
175177
const timeout = Math.max(timeUntilStale, 0)
176178

177179
this.staleTimeoutId = setTimeout(() => {
@@ -186,12 +188,7 @@ export class QueryObserver<TResult, TError> {
186188

187189
this.clearRefetchInterval()
188190

189-
if (
190-
!this.config.enabled ||
191-
!this.config.refetchInterval ||
192-
this.config.refetchInterval < 0 ||
193-
this.config.refetchInterval === Infinity
194-
) {
191+
if (!this.config.enabled || !isValidTimeout(this.config.refetchInterval)) {
195192
return
196193
}
197194

@@ -309,6 +306,8 @@ export class QueryObserver<TResult, TError> {
309306
}
310307

311308
onQueryUpdate(action: Action<TResult, TError>): void {
309+
const { type } = action
310+
312311
// Store current result and get new result
313312
const prevResult = this.currentResult
314313
this.updateResult()
@@ -317,11 +316,11 @@ export class QueryObserver<TResult, TError> {
317316

318317
// We need to check the action because the state could have
319318
// transitioned from success to success in case of `setQueryData`.
320-
if (action.type === 'Success' && currentResult.isSuccess) {
319+
if (type === 2) {
321320
config.onSuccess?.(currentResult.data!)
322321
config.onSettled?.(currentResult.data!, null)
323322
this.updateTimers()
324-
} else if (action.type === 'Error' && currentResult.isError) {
323+
} else if (type === 3) {
325324
config.onError?.(currentResult.error!)
326325
config.onSettled?.(undefined, currentResult.error!)
327326
this.updateTimers()

src/core/tests/utils.test.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { deepEqual, replaceEqualDeep } from '../utils'
1+
import { deepEqual, replaceEqualDeep, deepIncludes } from '../utils'
22
import { setConsole, queryCache } from '..'
33
import { queryKey } from '../../react/tests/utils'
44

@@ -29,6 +29,20 @@ describe('core/utils', () => {
2929
setConsole(console)
3030
})
3131

32+
describe('deepIncludes', () => {
33+
it('should return `true` if a includes b', () => {
34+
const a = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }
35+
const b = { a: { b: 'b' }, c: 'c', d: [] }
36+
expect(deepIncludes(a, b)).toEqual(true)
37+
})
38+
39+
it('should return `false` if a does not include b', () => {
40+
const a = { a: { b: 'b' }, c: 'c', d: [] }
41+
const b = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }
42+
expect(deepIncludes(a, b)).toEqual(false)
43+
})
44+
})
45+
3246
describe('deepEqual', () => {
3347
it('should return `true` for equal objects', () => {
3448
const a = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }

src/core/utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function functionalUpdate<TInput, TOutput>(
5454

5555
function stableStringifyReplacer(_key: string, value: any): unknown {
5656
if (typeof value === 'function') {
57-
throw new Error('Cannot stringify non JSON value')
57+
throw new Error()
5858
}
5959

6060
if (isObject(value)) {
@@ -89,6 +89,10 @@ export function deepIncludes(a: any, b: any): boolean {
8989
return false
9090
}
9191

92+
export function isValidTimeout(value: any): value is number {
93+
return typeof value === 'number' && value >= 0 && value !== Infinity
94+
}
95+
9296
export function isDocumentVisible(): boolean {
9397
// document global can be unavailable in react native
9498
if (typeof document === 'undefined') {

src/hydration/tests/hydration.test.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ describe('dehydration and rehydration', () => {
134134
// Exact shape is not important here, just that staleTime and cacheTime
135135
// (and any future other config) is not included in it
136136
const dehydratedQuery = dehydrated?.queries.find(
137-
dehydratedQuery =>
138-
(dehydratedQuery?.config?.queryKey as Array<string>)[0] === 'string'
137+
query => (query?.config?.queryKey as Array<string>)[0] === 'string'
139138
)
140139
expect(dehydratedQuery).toBeTruthy()
141140
expect(dehydratedQuery?.config.cacheTime).toBe(undefined)
@@ -180,8 +179,7 @@ describe('dehydration and rehydration', () => {
180179
// This is testing implementation details that can change and are not
181180
// part of the public API, but is important for keeping the payload small
182181
const dehydratedQuery = dehydrated?.queries.find(
183-
dehydratedQuery =>
184-
(dehydratedQuery?.config?.queryKey as Array<string>)[0] === 'string'
182+
query => (query?.config?.queryKey as Array<string>)[0] === 'string'
185183
)
186184
expect(dehydratedQuery).toBeUndefined()
187185

src/react/ReactQueryCacheProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from '../core'
88
import { QueryCache } from '../core/queryCache'
99

10-
export const queryCacheContext = React.createContext(defaultQueryCache)
10+
const queryCacheContext = React.createContext(defaultQueryCache)
1111

1212
export const useQueryCache = () => React.useContext(queryCacheContext)
1313

0 commit comments

Comments
 (0)