Skip to content

♻️ Refactor Focus Manager Event Handling for Enhanced User Experience ✨ #2672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 0 additions & 2 deletions _internal/src/utils/web-preset.ts
Original file line number Diff line number Diff line change
@@ -31,12 +31,10 @@ const initFocus = (callback: () => void) => {
if (isDocumentDefined) {
document.addEventListener('visibilitychange', callback)
}
onWindowEvent('focus', callback)
return () => {
if (isDocumentDefined) {
document.removeEventListener('visibilitychange', callback)
}
offWindowEvent('focus', callback)
}
}

5 changes: 1 addition & 4 deletions test/unit/web-preset.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { EventEmitter } from 'events'

const FOCUS_EVENT = 'focus'
const VISIBILITYCHANGE_EVENT = 'visibilitychange'

function createEventTarget() {
@@ -13,8 +12,7 @@ function createEventTarget() {

function runTests(propertyName) {
let initFocus
const eventName =
propertyName === 'window' ? FOCUS_EVENT : VISIBILITYCHANGE_EVENT
const eventName = VISIBILITYCHANGE_EVENT

describe(`Web Preset ${propertyName}`, () => {
const globalSpy = {
@@ -88,5 +86,4 @@ function runTests(propertyName) {
})
}

runTests('window')
runTests('document')
12 changes: 6 additions & 6 deletions test/use-swr-cache.test.tsx
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import {
createKey,
createResponse,
nextTick,
focusOn,
toggleVisibility,
renderWithConfig,
renderWithGlobalCache
} from './utils'
@@ -166,7 +166,7 @@ describe('useSWR - cache provider', () => {
await screen.findByText('1')
await nextTick()
// try to trigger revalidation, but shouldn't work
await focusOn(window)
toggleVisibility()
// revalidateOnFocus won't work
screen.getByText('1')
unmount()
@@ -175,7 +175,7 @@ describe('useSWR - cache provider', () => {
expect(unsubscribeReconnectFn).toBeCalledTimes(1)
})

it('should work with revalidateOnFocus', async () => {
it('should work with revalidateOnFocus 1', async () => {
const key = createKey()
let value = 0
function Page() {
@@ -190,7 +190,7 @@ describe('useSWR - cache provider', () => {

await screen.findByText('0')
await nextTick()
await focusOn(window)
toggleVisibility()
await nextTick()
screen.getByText('1')
})
@@ -397,7 +397,7 @@ describe('useSWR - global cache', () => {
await screen.findByText('data:mutated value')
})

it('should work with revalidateOnFocus', async () => {
it('should work with revalidateOnFocus 2', async () => {
const key = createKey()
let value = 0
function Page() {
@@ -411,7 +411,7 @@ describe('useSWR - global cache', () => {

await screen.findByText('0')
await nextTick()
await focusOn(window)
toggleVisibility()
await nextTick()
screen.getByText('1')
})
37 changes: 17 additions & 20 deletions test/use-swr-focus.test.tsx
Original file line number Diff line number Diff line change
@@ -4,13 +4,11 @@ import useSWR from 'swr'
import {
sleep,
nextTick as waitForNextTick,
focusOn,
toggleVisibility,
renderWithConfig,
createKey
} from './utils'

const focusWindow = () => focusOn(window)

describe('useSWR - focus', () => {
it('should revalidate on focus by default', async () => {
let value = 0
@@ -30,8 +28,7 @@ describe('useSWR - focus', () => {

await waitForNextTick()
// trigger revalidation
await focusWindow()

toggleVisibility()
await screen.findByText('data: 1')
})

@@ -56,7 +53,7 @@ describe('useSWR - focus', () => {

await waitForNextTick()
// trigger revalidation
await focusWindow()
toggleVisibility()
// should not be revalidated
screen.getByText('data: 0')
})
@@ -84,28 +81,28 @@ describe('useSWR - focus', () => {

await waitForNextTick()
// trigger revalidation
await focusWindow()
toggleVisibility()
// data should not change
screen.getByText('data: 0')

// change revalidateOnFocus to true
fireEvent.click(screen.getByText('data: 0'))
// trigger revalidation
await focusWindow()
toggleVisibility()
// data should update
await screen.findByText('data: 1')

await waitForNextTick()
// trigger revalidation
await focusWindow()
toggleVisibility()
// data should update
await screen.findByText('data: 2')

await waitForNextTick()
// change revalidateOnFocus to false
fireEvent.click(screen.getByText('data: 2'))
// trigger revalidation
await focusWindow()
toggleVisibility()
// data should not change
screen.getByText('data: 2')
})
@@ -132,17 +129,17 @@ describe('useSWR - focus', () => {

await waitForNextTick()
// trigger revalidation
await focusWindow()
toggleVisibility()
// still in throttling interval
await act(() => sleep(20))
// should be throttled
await focusWindow()
toggleVisibility()
await screen.findByText('data: 1')
// wait for focusThrottleInterval
await act(() => sleep(100))

// trigger revalidation again
await focusWindow()
toggleVisibility()
await screen.findByText('data: 2')
})

@@ -169,11 +166,11 @@ describe('useSWR - focus', () => {

await waitForNextTick()
// trigger revalidation
await focusWindow()
toggleVisibility()
// wait for throttle interval
await act(() => sleep(100))
// trigger revalidation
await focusWindow()
toggleVisibility()
await screen.findByText('data: 2')

await waitForNextTick()
@@ -182,17 +179,17 @@ describe('useSWR - focus', () => {
// wait for throttle interval
await act(() => sleep(100))
// trigger revalidation
await focusWindow()
toggleVisibility()
// wait for throttle interval
await act(() => sleep(100))
// should be throttled
await focusWindow()
toggleVisibility()
await screen.findByText('data: 3')

// wait for throttle interval
await act(() => sleep(150))
// trigger revalidation
await focusWindow()
toggleVisibility()
// wait for throttle intervals
await act(() => sleep(150))
await screen.findByText('data: 4')
@@ -214,7 +211,7 @@ describe('useSWR - focus', () => {
screen.getByText('data:')
await screen.findByText('data: 0')
await waitForNextTick()
await focusWindow()
toggleVisibility()
await screen.findByText('data: 1')
})

@@ -233,7 +230,7 @@ describe('useSWR - focus', () => {
renderWithConfig(<Page />)
await waitForNextTick()

fireEvent.focus(window)
toggleVisibility()
fireEvent.click(screen.getByText('change key'))

await waitForNextTick()
6 changes: 2 additions & 4 deletions test/use-swr-immutable.test.tsx
Original file line number Diff line number Diff line change
@@ -6,12 +6,10 @@ import {
sleep,
createKey,
nextTick as waitForNextTick,
focusOn,
toggleVisibility,
renderWithConfig
} from './utils'

const focusWindow = () => focusOn(window)

describe('useSWR - immutable', () => {
it('should revalidate on mount', async () => {
let value = 0
@@ -138,7 +136,7 @@ describe('useSWR - immutable', () => {

// trigger window focus
await waitForNextTick()
await focusWindow()
toggleVisibility()

// wait for rerender
await act(() => sleep(50))
5 changes: 3 additions & 2 deletions test/use-swr-integration.test.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@ import {
nextTick as waitForNextTick,
renderWithConfig,
createKey,
renderWithGlobalCache
renderWithGlobalCache,
toggleVisibility
} from './utils'

describe('useSWR', () => {
@@ -389,7 +390,7 @@ describe('useSWR', () => {
await screen.findByText('hello, Initial')

await waitForNextTick()
fireEvent.focus(window)
toggleVisibility()

await screen.findByText(`hello, ${initialKey}`)

15 changes: 5 additions & 10 deletions test/use-swr-offline.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { act, screen } from '@testing-library/react'
import { screen } from '@testing-library/react'
import useSWR from 'swr'
import {
nextTick as waitForNextTick,
focusOn,
toggleVisibility,
createKey,
renderWithConfig
renderWithConfig,
dispatchWindowEvent
} from './utils'

const focusWindow = () => focusOn(window)
const dispatchWindowEvent = event =>
act(async () => {
window.dispatchEvent(new Event(event))
})

describe('useSWR - offline', () => {
it('should not revalidate when offline', async () => {
let value = 0
@@ -36,7 +31,7 @@ describe('useSWR - offline', () => {
await dispatchWindowEvent('offline')

// trigger focus revalidation
await focusWindow()
toggleVisibility()

// should not be revalidated
screen.getByText('data: 0')
13 changes: 9 additions & 4 deletions test/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, fireEvent, render } from '@testing-library/react'
import { act, render } from '@testing-library/react'
import { SWRConfig } from 'swr'

export function sleep(time: number) {
@@ -21,9 +21,14 @@ export const createResponse = <T,>(

export const nextTick = () => act(() => sleep(1))

export const focusOn = (element: any) =>
act(async () => {
fireEvent.focus(element)
export const toggleVisibility = (_?: any) =>
act(() => {
document.dispatchEvent(new Event('visibilitychange'))
})

export const dispatchWindowEvent = (event: string) =>
act(() => {
window.dispatchEvent(new Event(event))
})

export const createKey = () => 'swr-key-' + ~~(Math.random() * 1e7)