Repository: tannerlinsley/react-query
Pull request #3103 (2021-12-16 18:54:26)

feat: remove logging in prod mode

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/tanstack/react-query/DXqmpqsZLALP2Coac4CrM7P7Yv8y](https://vercel.com/tanstack/react-query/DXqmpqsZLALP2Coac4CrM7P7Yv8y)  
✅ Preview: *In Progress*


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/tannerlinsley/react-query/pr/3103/builds/200413) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 34330453cf46c845b2498d151bab71e7d74e98f9:

|Sandbox| Source |
|--|--|
|[tannerlinsley/react-query: basic](https://codesandbox.io/s/tannerlinsley-react-query-basic-75y09)| Configuration |
|[tannerlinsley/react-query: basic-typescript](https://codesandbox.io/s/tannerlinsley-react-query-basic-typescript-svbdq)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) Report
> Merging [#3103](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (3433045) into [alpha](https://codecov.io/gh/tannerlinsley/react-query/commit/84b06ec1942a5c0170b006b5c6befa5fd9ff8a14?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (84b06ec) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/graphs/tree.svg?width=650&height=150&src=pr&token=jqEbswkEDQ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)

```diff
@@            Coverage Diff             @@
##            alpha    #3103      +/-   ##
==========================================
+ Coverage   96.81%   96.82%   +0.01%     
==========================================
  Files          46       46              
  Lines        2292     2300       +8     
  Branches      661      668       +7     
==========================================
+ Hits         2219     2227       +8     
  Misses         70       70              
  Partials        3        3              
```


| [Impacted Files](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) | Coverage Δ | |
|---|---|---|
| [src/core/mutation.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvbXV0YXRpb24udHM=) | `100.00% <100.00%> (ø)` | |
| [src/core/query.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvcXVlcnkudHM=) | `100.00% <100.00%> (ø)` | |
| [src/core/queryObserver.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvcXVlcnlPYnNlcnZlci50cw==) | `100.00% <100.00%> (ø)` | |
| [src/core/removable.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NvcmUvcmVtb3ZhYmxlLnRz) | `100.00% <100.00%> (ø)` | |
| [src/createWebStoragePersister/index.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3103/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL2NyZWF0ZVdlYlN0b3JhZ2VQZXJzaXN0ZXIvaW5kZXgudHM=) | `79.54% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Last update [84b06ec...3433045](https://codecov.io/gh/tannerlinsley/react-query/pull/3103?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).


Patch:
diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md
index 6d1535a19..8bffe466f 100644
--- a/docs/src/pages/guides/migrating-to-react-query-4.md
+++ b/docs/src/pages/guides/migrating-to-react-query-4.md
@@ -247,6 +247,11 @@ The methods `cancelMutatations` and `executeMutation` were undocumented and unus
 
 Types now require using TypeScript v4.1 or greater
 
+### Logging
+
+Starting with v4, react-query will no longer log errors (e.g. failed fetches) to the console in production mode, as this was confusing to many.
+Errors will still show up in development mode.
+
 ## New Features 🚀
 
 ### Proper offline support
diff --git a/src/core/mutation.ts b/src/core/mutation.ts
index 04533c11e..cc0f1e80d 100644
--- a/src/core/mutation.ts
+++ b/src/core/mutation.ts
@@ -235,8 +235,9 @@ export class Mutation<
           this as Mutation<unknown, unknown, unknown, unknown>
         )
 
-        // Log error
-        getLogger().error(error)
+        if (process.env.NODE_ENV !== 'production') {
+          getLogger().error(error)
+        }
 
         return Promise.resolve()
           .then(() =>
diff --git a/src/core/query.ts b/src/core/query.ts
index 07e171ecc..f82b0281f 100644
--- a/src/core/query.ts
+++ b/src/core/query.ts
@@ -367,6 +367,16 @@ export class Query<
       }
     }
 
+    if (
+      process.env.NODE_ENV !== 'production' &&
+      !Array.isArray(this.options.queryKey)
+    ) {
+      getLogger().error(
+        'As of v4, queryKey needs to be an Array, but the queryKey used was:',
+        JSON.stringify(this.options.queryKey)
+      )
+    }
+
     const abortController = getAbortController()
 
     // Create query function context
@@ -459,8 +469,9 @@ export class Query<
           // Notify cache callback
           this.cache.config.onError?.(error, this as Query<any, any, any, any>)
 
-          // Log error
-          getLogger().error(error)
+          if (process.env.NODE_ENV !== 'production') {
+            getLogger().error(error)
+          }
         }
 
         if (!this.isFetchingOptimistic) {
diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts
index 062499912..c2a249a9a 100644
--- a/src/core/queryObserver.ts
+++ b/src/core/queryObserver.ts
@@ -70,8 +70,8 @@ export class QueryObserver<
   private previousQueryResult?: QueryObserverResult<TData, TError>
   private previousSelectError: TError | null
   private previousSelectFn?: (data: TQueryData) => TData
-  private staleTimeoutId?: number
-  private refetchIntervalId?: number
+  private staleTimeoutId?: ReturnType<typeof setTimeout>
+  private refetchIntervalId?: ReturnType<typeof setInterval>
   private currentRefetchInterval?: number | false
   private trackedProps!: Set<keyof QueryObserverResult>
 
@@ -398,12 +398,12 @@ export class QueryObserver<
   }
 
   private clearStaleTimeout(): void {
-    clearTimeout(this.staleTimeoutId)
+    clearTimeout(this.staleTimeoutId!)
     this.staleTimeoutId = undefined
   }
 
   private clearRefetchInterval(): void {
-    clearInterval(this.refetchIntervalId)
+    clearInterval(this.refetchIntervalId!)
     this.refetchIntervalId = undefined
   }
 
@@ -486,7 +486,9 @@ export class QueryObserver<
           }
           this.previousSelectError = null
         } catch (selectError) {
-          getLogger().error(selectError)
+          if (process.env.NODE_ENV !== 'production') {
+            getLogger().error(selectError)
+          }
           error = selectError as TError
           this.previousSelectError = selectError as TError
           errorUpdatedAt = Date.now()
@@ -529,7 +531,9 @@ export class QueryObserver<
             }
             this.previousSelectError = null
           } catch (selectError) {
-            getLogger().error(selectError)
+            if (process.env.NODE_ENV !== 'production') {
+              getLogger().error(selectError)
+            }
             error = selectError as TError
             this.previousSelectError = selectError as TError
             errorUpdatedAt = Date.now()
diff --git a/src/core/removable.ts b/src/core/removable.ts
index c33f08202..eea3e682b 100644
--- a/src/core/removable.ts
+++ b/src/core/removable.ts
@@ -2,7 +2,7 @@ import { isValidTimeout } from './utils'
 
 export abstract class Removable {
   cacheTime!: number
-  private gcTimeout?: number
+  private gcTimeout?: ReturnType<typeof setTimeout>
 
   destroy(): void {
     this.clearGcTimeout()
@@ -27,7 +27,7 @@ export abstract class Removable {
   }
 
   protected clearGcTimeout() {
-    clearTimeout(this.gcTimeout)
+    clearTimeout(this.gcTimeout!)
     this.gcTimeout = undefined
   }
 
diff --git a/src/createAsyncStoragePersister/index.ts b/src/createAsyncStoragePersister/index.ts
index 96731076c..9a1b7b667 100644
--- a/src/createAsyncStoragePersister/index.ts
+++ b/src/createAsyncStoragePersister/index.ts
@@ -58,7 +58,7 @@ function asyncThrottle<Args extends readonly unknown[], Result>(
   if (typeof func !== 'function') throw new Error('argument is not function.')
   const running = { current: false }
   let lastTime = 0
-  let timeout: number
+  let timeout: ReturnType<typeof setTimeout>
   const queue: Array<Args> = []
   return (...args: Args) =>
     (async () => {
diff --git a/src/createWebStoragePersister/index.ts b/src/createWebStoragePersister/index.ts
index e2f61c1d6..a6493b707 100644
--- a/src/createWebStoragePersister/index.ts
+++ b/src/createWebStoragePersister/index.ts
@@ -96,7 +96,7 @@ function throttle<TArgs extends any[]>(
   func: (...args: TArgs) => any,
   wait = 100
 ) {
-  let timer: number | null = null
+  let timer: ReturnType<typeof setTimeout> | null = null
   let params: TArgs
   return function (...args: TArgs) {
     params = args
diff --git a/tsconfig.json b/tsconfig.json
index c88aaec9b..34a78d8b1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,7 +14,7 @@
     "noUncheckedIndexedAccess": true,
     "skipLibCheck": true,
     "strict": true,
-    "types": ["jest"],
+    "types": ["jest", "node"],
     "paths": {
       "react-query": ["./src/index.ts"]
     }


Test patch:
diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx
index 7bf5eef37..27c1d0fdf 100644
--- a/src/core/tests/queriesObserver.test.tsx
+++ b/src/core/tests/queriesObserver.test.tsx
@@ -5,6 +5,8 @@ import {
   QueriesObserver,
   QueryObserverResult,
   QueryObserver,
+  Logger,
+  setLogger,
 } from '../..'
 import { QueryKey } from '..'
 
@@ -39,6 +41,14 @@ describe('queriesObserver', () => {
   })
 
   test('should still return value for undefined query key', async () => {
+    const logger: Logger = {
+      error: jest.fn(),
+      log: jest.fn(),
+      warn: jest.fn(),
+    }
+
+    setLogger(logger)
+
     const key1 = queryKey()
     const queryFn1 = jest.fn().mockReturnValue(1)
     const queryFn2 = jest.fn().mockReturnValue(2)
@@ -53,6 +63,9 @@ describe('queriesObserver', () => {
     await sleep(1)
     unsubscribe()
     expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
+
+    expect(logger.error).toHaveBeenCalledTimes(1)
+    setLogger(console)
   })
 
   test('should update when a query updates', async () => {
diff --git a/src/core/tests/utils.test.tsx b/src/core/tests/utils.test.tsx
index 2297c2a6b..af8b618a8 100644
--- a/src/core/tests/utils.test.tsx
+++ b/src/core/tests/utils.test.tsx
@@ -379,7 +379,7 @@ describe('core/utils', () => {
             // Do no throw an uncaught exception that cannot be tested with
             // this jest version
           }
-          return 0
+          return 0 as any
         })
       scheduleMicrotask(callback)
       jest.runAllTimers()