Repository: zeit/swr (ts)

Issue:
Explicit target for online event
Hi, currently we subscribe and unsubscribe to `'online'` event using global `addEventListener` and `removeEventListener`

Unfortunately there seems to be a Chrome bug triggered by Sentry's SDK which leads to `TypeError`s when `this` is `undefined`. More info in [this issue](https://github.com/getsentry/sentry-javascript/issues/2074).
It seems like bug is pretty tricky to fix on Sentry's side, but it's pretty easy to fix on SWR's side. I've created a PR with a [fix](https://github.com/zeit/swr/pull/300)

Also we leak subscriptions, because the assignment to reconnect is wrong, but that seems to be addressed in [this PR](https://github.com/zeit/swr/pull/297)

Pull request description:
Use window as a target for online event
Also fix unsubscribe callback assignment

Closes #301 

Patch:
diff --git a/src/use-swr.ts b/src/use-swr.ts
index c11b113..701b822 100644
--- a/src/use-swr.ts
+++ b/src/use-swr.ts
@@ -40,8 +40,6 @@ const IS_SERVER = typeof window === 'undefined'
 // useLayoutEffect in the browser.
 const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect
 
-const NO_DEDUPE = false
-
 const trigger: triggerInterface = (_key, shouldRevalidate = true) => {
   // we are ignoring the second argument which correspond to the arguments
   // the fetcher will receive when key is an array
@@ -53,7 +51,7 @@ const trigger: triggerInterface = (_key, shouldRevalidate = true) => {
     const currentData = cache.get(key)
     const currentError = cache.get(keyErr)
     for (let i = 0; i < updaters.length; ++i) {
-      updaters[i](shouldRevalidate, currentData, currentError, NO_DEDUPE)
+      updaters[i](shouldRevalidate, currentData, currentError, i > 0)
     }
   }
 }
@@ -110,7 +108,7 @@ const mutate: mutateInterface = async (
   const updaters = CACHE_REVALIDATORS[key]
   if (updaters) {
     for (let i = 0; i < updaters.length; ++i) {
-      updaters[i](!!shouldRevalidate, data, error, NO_DEDUPE)
+      updaters[i](!!shouldRevalidate, data, error, i > 0)
     }
   }
 
@@ -464,11 +462,8 @@ function useSWR<Data = any, Error = any>(
 
     // set up reconnecting when the browser regains network connection
     let reconnect = null
-    if (
-      typeof addEventListener !== 'undefined' &&
-      config.revalidateOnReconnect
-    ) {
-      addEventListener('online', (reconnect = softRevalidate))
+    if (!IS_SERVER && window.addEventListener && config.revalidateOnReconnect) {
+      window.addEventListener('online', (reconnect = softRevalidate))
     }
 
     return () => {
@@ -497,8 +492,8 @@ function useSWR<Data = any, Error = any>(
         }
       }
 
-      if (typeof removeEventListener !== 'undefined' && reconnect !== null) {
-        removeEventListener('online', reconnect)
+      if (!IS_SERVER && window.removeEventListener && reconnect !== null) {
+        window.removeEventListener('online', reconnect)
       }
     }
   }, [key, revalidate])


Test patch:
diff --git a/test/use-swr.test.tsx b/test/use-swr.test.tsx
index dae9801..a6d72d5 100644
--- a/test/use-swr.test.tsx
+++ b/test/use-swr.test.tsx
@@ -806,7 +806,7 @@ describe('useSWR - local mutation', () => {
     expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`)
   })
 
-  it('should trigger revalidation programmatically with a dedupingInterval', async () => {
+  it('should trigger revalidation programmatically within a dedupingInterval', async () => {
     let value = 0
 
     function Page() {
@@ -852,13 +852,16 @@ describe('useSWR - local mutation', () => {
     expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`)
   })
 
-  it('should mutate the cache and revalidate with a dedupingInterval', async () => {
+  it('should dedupe extra requests after mutation', async () => {
     let value = 0
 
     function Page() {
       const { data } = useSWR('dynamic-13', () => value++, {
         dedupingInterval: 2000
       })
+      useSWR('dynamic-13', () => value++, {
+        dedupingInterval: 2000
+      })
       return <div>data: {data}</div>
     }
     const { container } = render(<Page />)
@@ -869,7 +872,7 @@ describe('useSWR - local mutation', () => {
     expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 0"`)
     await act(() => {
       // mutate and revalidate
-      mutate('dynamic-13', 'mutate')
+      mutate('dynamic-13')
       return new Promise(res => setTimeout(res, 1))
     })
     expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`)