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
37 changes: 13 additions & 24 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,33 +231,22 @@ export class Query<TResult, TError> {
)
}

onWindowFocus(): void {
if (
this.observers.some(
observer =>
observer.isStale() &&
observer.config.enabled &&
observer.config.refetchOnWindowFocus
)
) {
this.fetch().catch(noop)
}

this.continue()
}
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(
observer =>
observer.isStale() &&
observer.config.enabled &&
((observer.config.refetchOnWindowFocus && type === 'focus') ||
(observer.config.refetchOnReconnect && type === 'online'))
)

onOnline(): void {
if (
this.observers.some(
observer =>
observer.isStale() &&
observer.config.enabled &&
observer.config.refetchOnReconnect
)
) {
this.fetch().catch(noop)
if (observer) {
observer.fetch().catch(noop)
}

// Continue any paused fetch
this.continue()
}

Expand Down
8 changes: 2 additions & 6 deletions src/core/queryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,15 +354,11 @@ export function makeQueryCache(config?: QueryCacheConfig) {
return new QueryCache(config)
}

export function onVisibilityOrOnlineChange(isOnlineChange: boolean) {
export function onVisibilityOrOnlineChange(type: 'focus' | 'online') {
if (isDocumentVisible() && isOnline()) {
queryCaches.forEach(queryCache => {
queryCache.getQueries().forEach(query => {
if (isOnlineChange) {
query.onOnline()
} else {
query.onWindowFocus()
}
query.onInteraction(type)
})
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/setFocusHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createSetHandler, isServer } from './utils'
import { onVisibilityOrOnlineChange } from './queryCache'

export const setFocusHandler = createSetHandler(() =>
onVisibilityOrOnlineChange(false)
onVisibilityOrOnlineChange('focus')
)

setFocusHandler(handleFocus => {
Expand Down
2 changes: 1 addition & 1 deletion src/core/setOnlineHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createSetHandler, isServer } from './utils'
import { onVisibilityOrOnlineChange } from './queryCache'

export const setOnlineHandler = createSetHandler(() =>
onVisibilityOrOnlineChange(true)
onVisibilityOrOnlineChange('online')
)

setOnlineHandler(handleOnline => {
Expand Down
151 changes: 106 additions & 45 deletions src/react/tests/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ describe('useQuery', () => {

await queryCache.prefetchQuery(key, () => 'prefetch')

await sleep(40)
await sleep(20)

function FirstComponent() {
const state = useQuery(key, () => 'one', {
Expand All @@ -656,7 +656,7 @@ describe('useQuery', () => {

function SecondComponent() {
const state = useQuery(key, () => 'two', {
staleTime: 20,
staleTime: 10,
})
states2.push(state)
return null
Expand All @@ -673,50 +673,55 @@ describe('useQuery', () => {

render(<Page />)

await waitFor(() => expect(states1.length).toBe(4))
await waitFor(() => expect(states2.length).toBe(4))

// First render
expect(states1[0]).toMatchObject({
data: 'prefetch',
isStale: false,
})
// Second useQuery started fetching
expect(states1[1]).toMatchObject({
data: 'prefetch',
isStale: false,
})
// Second useQuery data came in
expect(states1[2]).toMatchObject({
data: 'two',
isStale: false,
})
// Data became stale after 100ms
expect(states1[3]).toMatchObject({
data: 'two',
isStale: true,
})
await waitFor(() =>
expect(states1).toMatchObject([
// First render
{
data: 'prefetch',
isStale: false,
},
// Second useQuery started fetching
{
data: 'prefetch',
isStale: false,
},
// Second useQuery data came in
{
data: 'two',
isStale: false,
},
// Data became stale after 100ms
{
data: 'two',
isStale: true,
},
])
)

// First render, data is stale
expect(states2[0]).toMatchObject({
data: 'prefetch',
isStale: true,
})
// Second useQuery started fetching
expect(states2[1]).toMatchObject({
data: 'prefetch',
isStale: true,
})
// Second useQuery data came in
expect(states2[2]).toMatchObject({
data: 'two',
isStale: false,
})
// Data became stale after 5ms
expect(states2[3]).toMatchObject({
data: 'two',
isStale: true,
})
await waitFor(() =>
expect(states2).toMatchObject([
// First render, data is stale
{
data: 'prefetch',
isStale: true,
},
// Second useQuery started fetching
{
data: 'prefetch',
isStale: true,
},
// Second useQuery data came in
{
data: 'two',
isStale: false,
},
// Data became stale after 5ms
{
data: 'two',
isStale: true,
},
])
)
})

it('should re-render when a query becomes stale', async () => {
Expand Down Expand Up @@ -1220,6 +1225,62 @@ describe('useQuery', () => {
consoleMock.mockRestore()
})

it('should refetch after focus regain', async () => {
const key = queryKey()
const states: QueryResult<string>[] = []
const consoleMock = mockConsoleError()

// make page unfocused
const originalVisibilityState = document.visibilityState
mockVisibilityState('hidden')

// set data in cache to check if the hook query fn is actually called
queryCache.setQueryData(key, 'prefetched')

function Page() {
const state = useQuery(key, () => 'data')
states.push(state)
return null
}

render(<Page />)

await waitFor(() => expect(states.length).toBe(2))

act(() => {
// reset visibilityState to original value
mockVisibilityState(originalVisibilityState)
window.dispatchEvent(new FocusEvent('focus'))
})

await waitFor(() => expect(states.length).toBe(4))

expect(states).toMatchObject([
{
data: 'prefetched',
isFetching: false,
isStale: false,
},
{
data: 'prefetched',
isFetching: false,
isStale: true,
},
{
data: 'prefetched',
isFetching: true,
isStale: true,
},
{
data: 'data',
isFetching: false,
isStale: true,
},
])

consoleMock.mockRestore()
})

// See https://github.com/tannerlinsley/react-query/issues/195
it('should refetch if stale after a prefetch', async () => {
const key = queryKey()
Expand Down