Skip to content
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

feat: Ensure auto revalidations are executed after state updates #1720

Merged
merged 1 commit into from Dec 23, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 22 additions & 9 deletions src/utils/cache.ts
Expand Up @@ -41,9 +41,10 @@ export const initCache = <Data = any>(
// If there's no global state bound to the provider, create a new one with the
// new mutate function.
const EVENT_REVALIDATORS = {}
const mutate = internalMutate.bind(UNDEFINED, provider) as ScopedMutator<
Data
>
const mutate = internalMutate.bind(
UNDEFINED,
provider
) as ScopedMutator<Data>
let unmount = noop

// Update the state if it's new, or the provider has been extended.
Expand All @@ -60,18 +61,30 @@ export const initCache = <Data = any>(
// This is a new provider, we need to initialize it and setup DOM events
// listeners for `focus` and `reconnect` actions.
if (!IS_SERVER) {
// When listening to the native events for auto revalidations,
// we intentionally put a delay (setTimeout) here to make sure they are
// fired after immediate JavaScript executions, which can possibly be
// React's state updates.
// This avoids some unnecessary revalidations such as
// https://github.com/vercel/swr/issues/1680.
const releaseFocus = opts.initFocus(
revalidateAllKeys.bind(
setTimeout.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.FOCUS_EVENT
revalidateAllKeys.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.FOCUS_EVENT
)
)
)
const releaseReconnect = opts.initReconnect(
revalidateAllKeys.bind(
setTimeout.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.RECONNECT_EVENT
revalidateAllKeys.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.RECONNECT_EVENT
)
)
)
unmount = () => {
Expand Down
14 changes: 7 additions & 7 deletions src/utils/web-preset.ts
Expand Up @@ -36,21 +36,21 @@ const isVisible = () => {
return true
}

const initFocus = (cb: () => void) => {
const initFocus = (callback: () => void) => {
// focus revalidate
onDocumentEvent('visibilitychange', cb)
onWindowEvent('focus', cb)
onDocumentEvent('visibilitychange', callback)
onWindowEvent('focus', callback)
return () => {
offDocumentEvent('visibilitychange', cb)
offWindowEvent('focus', cb)
offDocumentEvent('visibilitychange', callback)
offWindowEvent('focus', callback)
}
}

const initReconnect = (cb: () => void) => {
const initReconnect = (callback: () => void) => {
// revalidate on reconnected
const onOnline = () => {
online = true
cb()
callback()
}
// nothing to revalidate, just update the status
const onOffline = () => {
Expand Down
2 changes: 2 additions & 0 deletions test/use-swr-cache.test.tsx
Expand Up @@ -183,6 +183,7 @@ describe('useSWR - cache provider', () => {
await screen.findByText('0')
await nextTick()
await focusOn(window)
await nextTick()
screen.getByText('1')
})

Expand Down Expand Up @@ -396,6 +397,7 @@ describe('useSWR - global cache', () => {
await screen.findByText('0')
await nextTick()
await focusOn(window)
await nextTick()
screen.getByText('1')
})

Expand Down
25 changes: 25 additions & 0 deletions test/use-swr-focus.test.tsx
Expand Up @@ -220,4 +220,29 @@ describe('useSWR - focus', () => {
await focusWindow()
await screen.findByText('data: 1')
})

it('should not revalidate on focus when key changes in the same tick', async () => {
const fetchLogs = []

function Page() {
const [key, setKey] = useState(() => createKey())
useSWR(key, k => fetchLogs.push(k), {
revalidateOnFocus: true,
dedupingInterval: 0
})
return <div onClick={() => setKey(createKey())}>change key</div>
}

renderWithConfig(<Page />)
await waitForNextTick()

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

await waitForNextTick()

// Only fetched twice with the initial and the new keys.
expect(fetchLogs.length).toBe(2)
expect(fetchLogs[0]).not.toEqual(fetchLogs[1])
})
})