From 7dd1f615b02da9c992c5d16eee9c753053e7526c Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Thu, 23 Dec 2021 01:45:06 +0100 Subject: [PATCH] fix #1680 --- src/utils/cache.ts | 31 ++++++++++++++++++++++--------- src/utils/web-preset.ts | 14 +++++++------- test/use-swr-cache.test.tsx | 2 ++ test/use-swr-focus.test.tsx | 25 +++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/utils/cache.ts b/src/utils/cache.ts index bbe88be09..088c7305f 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -41,9 +41,10 @@ export const initCache = ( // 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 let unmount = noop // Update the state if it's new, or the provider has been extended. @@ -60,18 +61,30 @@ export const initCache = ( // 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 = () => { diff --git a/src/utils/web-preset.ts b/src/utils/web-preset.ts index 6fd71c0f2..a631ab9fd 100644 --- a/src/utils/web-preset.ts +++ b/src/utils/web-preset.ts @@ -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 = () => { diff --git a/test/use-swr-cache.test.tsx b/test/use-swr-cache.test.tsx index 41984647e..405a47dfb 100644 --- a/test/use-swr-cache.test.tsx +++ b/test/use-swr-cache.test.tsx @@ -183,6 +183,7 @@ describe('useSWR - cache provider', () => { await screen.findByText('0') await nextTick() await focusOn(window) + await nextTick() screen.getByText('1') }) @@ -396,6 +397,7 @@ describe('useSWR - global cache', () => { await screen.findByText('0') await nextTick() await focusOn(window) + await nextTick() screen.getByText('1') }) diff --git a/test/use-swr-focus.test.tsx b/test/use-swr-focus.test.tsx index 59871977b..f9c82ca20 100644 --- a/test/use-swr-focus.test.tsx +++ b/test/use-swr-focus.test.tsx @@ -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
setKey(createKey())}>change key
+ } + + renderWithConfig() + 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]) + }) })