Repository: tannerlinsley/react-query
Pull request #895 (2020-08-20 15:02:07)

chore(actions): revert semantic release action code

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: *In Progress*  
✅ Preview: *In Progress*



Patch:
diff --git a/src/core/config.ts b/src/core/config.ts
index 67fa74af9..22c2df950 100644
--- a/src/core/config.ts
+++ b/src/core/config.ts
@@ -4,6 +4,8 @@ import {
   QueryKey,
   QueryKeySerializerFunction,
   ReactQueryConfig,
+  QueryConfig,
+  MutationConfig,
 } from './types'
 
 // TYPES
@@ -29,6 +31,22 @@ export const defaultQueryKeySerializerFn: QueryKeySerializerFunction = (
   }
 }
 
+/**
+ * Config merging strategy
+ *
+ * When using hooks the config will be merged in the following order:
+ *
+ * 1. These defaults.
+ * 2. Defaults from the hook query cache.
+ * 3. Combined defaults from any config providers in the tree.
+ * 4. Query/mutation config provided to the hook.
+ *
+ * When using a query cache directly the config will be merged in the following order:
+ *
+ * 1. These defaults.
+ * 2. Defaults from the query cache.
+ * 3. Query/mutation config provided to the query cache method.
+ */
 export const DEFAULT_CONFIG: ReactQueryConfig = {
   queries: {
     queryKeySerializerFn: defaultQueryKeySerializerFn,
@@ -42,6 +60,63 @@ export const DEFAULT_CONFIG: ReactQueryConfig = {
   },
 }
 
-export const defaultConfigRef: ReactQueryConfigRef = {
-  current: DEFAULT_CONFIG,
+export function mergeReactQueryConfigs(
+  a: ReactQueryConfig,
+  b: ReactQueryConfig
+): ReactQueryConfig {
+  return {
+    shared: {
+      ...a.shared,
+      ...b.shared,
+    },
+    queries: {
+      ...a.queries,
+      ...b.queries,
+    },
+    mutations: {
+      ...a.mutations,
+      ...b.mutations,
+    },
+  }
+}
+
+export function getDefaultedQueryConfig<TResult, TError>(
+  queryCacheConfig?: ReactQueryConfig,
+  contextConfig?: ReactQueryConfig,
+  config?: QueryConfig<TResult, TError>,
+  configOverrides?: QueryConfig<TResult, TError>
+): QueryConfig<TResult, TError> {
+  return {
+    ...DEFAULT_CONFIG.shared,
+    ...DEFAULT_CONFIG.queries,
+    ...queryCacheConfig?.shared,
+    ...queryCacheConfig?.queries,
+    ...contextConfig?.shared,
+    ...contextConfig?.queries,
+    ...config,
+    ...configOverrides,
+  } as QueryConfig<TResult, TError>
+}
+
+export function getDefaultedMutationConfig<
+  TResult,
+  TError,
+  TVariables,
+  TSnapshot
+>(
+  queryCacheConfig?: ReactQueryConfig,
+  contextConfig?: ReactQueryConfig,
+  config?: MutationConfig<TResult, TError, TVariables, TSnapshot>,
+  configOverrides?: MutationConfig<TResult, TError, TVariables, TSnapshot>
+): MutationConfig<TResult, TError, TVariables, TSnapshot> {
+  return {
+    ...DEFAULT_CONFIG.shared,
+    ...DEFAULT_CONFIG.mutations,
+    ...queryCacheConfig?.shared,
+    ...queryCacheConfig?.mutations,
+    ...contextConfig?.shared,
+    ...contextConfig?.mutations,
+    ...config,
+    ...configOverrides,
+  } as MutationConfig<TResult, TError, TVariables, TSnapshot>
 }
diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts
index 1e6700ee2..23892e6b9 100644
--- a/src/core/queryCache.ts
+++ b/src/core/queryCache.ts
@@ -6,7 +6,7 @@ import {
   isObject,
   Updater,
 } from './utils'
-import { defaultConfigRef, ReactQueryConfigRef } from './config'
+import { getDefaultedQueryConfig } from './config'
 import { Query } from './query'
 import {
   QueryConfig,
@@ -76,7 +76,6 @@ export class QueryCache {
   isFetching: number
 
   private config: QueryCacheConfig
-  private configRef: ReactQueryConfigRef
   private globalListeners: QueryCacheListener[]
 
   constructor(config?: QueryCacheConfig) {
@@ -85,25 +84,6 @@ export class QueryCache {
     // A frozen cache does not add new queries to the cache
     this.globalListeners = []
 
-    this.configRef = this.config.defaultConfig
-      ? {
-          current: {
-            shared: {
-              ...defaultConfigRef.current.shared,
-              ...this.config.defaultConfig.shared,
-            },
-            queries: {
-              ...defaultConfigRef.current.queries,
-              ...this.config.defaultConfig.queries,
-            },
-            mutations: {
-              ...defaultConfigRef.current.mutations,
-              ...this.config.defaultConfig.mutations,
-            },
-          },
-        }
-      : defaultConfigRef
-
     this.queries = {}
     this.isFetching = 0
   }
@@ -118,16 +98,15 @@ export class QueryCache {
   }
 
   getDefaultConfig() {
-    return this.configRef.current
+    return this.config.defaultConfig
   }
 
-  getDefaultedConfig<TResult, TError>(config?: QueryConfig<TResult, TError>) {
-    return {
-      ...this.configRef.current.shared!,
-      ...this.configRef.current.queries!,
+  getDefaultedQueryConfig<TResult, TError>(
+    config?: QueryConfig<TResult, TError>
+  ): QueryConfig<TResult, TError> {
+    return getDefaultedQueryConfig(this.getDefaultConfig(), undefined, config, {
       queryCache: this,
-      ...config,
-    } as QueryConfig<TResult, TError>
+    })
   }
 
   subscribe(listener: QueryCacheListener): () => void {
@@ -158,8 +137,8 @@ export class QueryCache {
     if (typeof predicate === 'function') {
       predicateFn = predicate as QueryPredicateFn
     } else {
-      const [queryHash, queryKey] = this.configRef.current.queries!
-        .queryKeySerializerFn!(predicate)
+      const config = this.getDefaultedQueryConfig()
+      const [queryHash, queryKey] = config.queryKeySerializerFn!(predicate)
 
       predicateFn = d =>
         options?.exact
@@ -234,7 +213,7 @@ export class QueryCache {
     userQueryKey: QueryKey,
     queryConfig?: QueryConfig<TResult, TError>
   ): Query<TResult, TError> {
-    const config = this.getDefaultedConfig(queryConfig)
+    const config = this.getDefaultedQueryConfig(queryConfig)
 
     const [queryHash, queryKey] = config.queryKeySerializerFn!(userQueryKey)
 
@@ -352,6 +331,7 @@ export class QueryCache {
       TError,
       PrefetchQueryOptions | undefined
     >(args)
+
     // https://github.com/tannerlinsley/react-query/issues/652
     const configWithoutRetry = { retry: false, ...config }
 
diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts
index 3e22560f8..0cec1786b 100644
--- a/src/core/queryObserver.ts
+++ b/src/core/queryObserver.ts
@@ -24,13 +24,14 @@ export class QueryObserver<TResult, TError> {
     this.refetch = this.refetch.bind(this)
     this.fetchMore = this.fetchMore.bind(this)
 
-    // Subscribe to query
+    // Subscribe to the query
     this.updateQuery()
   }
 
   subscribe(listener?: UpdateListener<TResult, TError>): () => void {
     this.started = true
     this.updateListener = listener
+    this.currentQuery.subscribeObserver(this)
     this.optionalFetch()
     this.updateRefetchInterval()
     return this.unsubscribe.bind(this)
@@ -204,11 +205,14 @@ export class QueryObserver<TResult, TError> {
     }
 
     this.previousResult = this.currentResult
-    prevQuery?.unsubscribeObserver(this)
     this.currentQuery = newQuery
-    newQuery.subscribeObserver(this)
     this.currentResult = this.createResult()
 
+    if (this.started) {
+      prevQuery?.unsubscribeObserver(this)
+      this.currentQuery.subscribeObserver(this)
+    }
+
     return true
   }
 
diff --git a/src/core/types.ts b/src/core/types.ts
index a3f1ec25d..7011b8484 100644
--- a/src/core/types.ts
+++ b/src/core/types.ts
@@ -215,6 +215,10 @@ export interface MutationConfig<
   onMutate?: (variables: TVariables) => Promise<TSnapshot> | TSnapshot
   useErrorBoundary?: boolean
   suspense?: boolean
+  /**
+   * By default the query cache from the context is used, but a different cache can be specified.
+   */
+  queryCache?: QueryCache
 }
 
 export type MutationFunction<TResult, TVariables = unknown> = (
diff --git a/src/react/ReactQueryConfigProvider.tsx b/src/react/ReactQueryConfigProvider.tsx
index b05064858..6e6d76c90 100644
--- a/src/react/ReactQueryConfigProvider.tsx
+++ b/src/react/ReactQueryConfigProvider.tsx
@@ -1,74 +1,34 @@
 import React from 'react'
-import { DEFAULT_CONFIG, defaultConfigRef } from '../core/config'
+
+import { mergeReactQueryConfigs } from '../core/config'
 import { ReactQueryConfig } from '../core/types'
-import { useQueryCache } from './ReactQueryCacheProvider'
 
 const configContext = React.createContext<ReactQueryConfig | undefined>(
   undefined
 )
 
-export function useConfigContext() {
-  const queryCache = useQueryCache()
-  return (
-    React.useContext(configContext) ||
-    queryCache.getDefaultConfig() ||
-    defaultConfigRef.current
-  )
+export function useContextConfig() {
+  return React.useContext(configContext)
 }
 
-export interface ReactQueryProviderConfig extends ReactQueryConfig {}
-
 export interface ReactQueryConfigProviderProps {
-  config: ReactQueryProviderConfig
+  config: ReactQueryConfig
 }
 
 export const ReactQueryConfigProvider: React.FC<ReactQueryConfigProviderProps> = ({
   config,
   children,
 }) => {
-  const configContextValueOrDefault = useConfigContext()
-  const configContextValue = React.useContext(configContext)
+  const parentConfig = useContextConfig()
 
-  const newConfig = React.useMemo<ReactQueryConfig>(() => {
-    const { shared = {}, queries = {}, mutations = {} } = config
-    const {
-      shared: contextShared = {},
-      queries: contextQueries = {},
-      mutations: contextMutations = {},
-    } = configContextValueOrDefault
-
-    return {
-      shared: {
-        ...contextShared,
-        ...shared,
-      },
-      queries: {
-        ...contextQueries,
-        ...queries,
-      },
-      mutations: {
-        ...contextMutations,
-        ...mutations,
-      },
-    }
-  }, [config, configContextValueOrDefault])
-
-  React.useEffect(() => {
-    // restore previous config on unmount
-    return () => {
-      defaultConfigRef.current = {
-        ...(configContextValueOrDefault || DEFAULT_CONFIG),
-      }
-    }
-  }, [configContextValueOrDefault])
-
-  // If this is the outermost provider, overwrite the shared default config
-  if (!configContextValue) {
-    defaultConfigRef.current = newConfig
-  }
+  const mergedConfig = React.useMemo(
+    () =>
+      parentConfig ? mergeReactQueryConfigs(parentConfig, config) : config,
+    [config, parentConfig]
+  )
 
   return (
-    <configContext.Provider value={newConfig}>
+    <configContext.Provider value={mergedConfig}>
       {children}
     </configContext.Provider>
   )
diff --git a/src/react/index.ts b/src/react/index.ts
index 88a62f58b..771b12952 100644
--- a/src/react/index.ts
+++ b/src/react/index.ts
@@ -17,7 +17,4 @@ export type { UseQueryObjectConfig } from './useQuery'
 export type { UseInfiniteQueryObjectConfig } from './useInfiniteQuery'
 export type { UsePaginatedQueryObjectConfig } from './usePaginatedQuery'
 export type { ReactQueryCacheProviderProps } from './ReactQueryCacheProvider'
-export type {
-  ReactQueryConfigProviderProps,
-  ReactQueryProviderConfig,
-} from './ReactQueryConfigProvider'
+export type { ReactQueryConfigProviderProps } from './ReactQueryConfigProvider'
diff --git a/src/react/useBaseQuery.ts b/src/react/useBaseQuery.ts
index 1910b29db..1a56cdd56 100644
--- a/src/react/useBaseQuery.ts
+++ b/src/react/useBaseQuery.ts
@@ -3,10 +3,13 @@ import React from 'react'
 import { useRerenderer } from './utils'
 import { QueryObserver } from '../core/queryObserver'
 import { QueryResultBase, QueryObserverConfig } from '../core/types'
+import { useDefaultedQueryConfig } from './useDefaultedQueryConfig'
 
 export function useBaseQuery<TResult, TError>(
   config: QueryObserverConfig<TResult, TError> = {}
 ): QueryResultBase<TResult, TError> {
+  config = useDefaultedQueryConfig(config)
+
   // Make a rerender function
   const rerender = useRerenderer()
 
@@ -39,6 +42,7 @@ export function useBaseQuery<TResult, TError>(
     }
 
     if (config.enabled && config.suspense && !result.isSuccess) {
+      observer.subscribe()
       throw observer.fetch().finally(() => {
         observer.unsubscribe(true)
       })
diff --git a/src/react/useDefaultedMutationConfig.tsx b/src/react/useDefaultedMutationConfig.tsx
new file mode 100644
index 000000000..c429b9489
--- /dev/null
+++ b/src/react/useDefaultedMutationConfig.tsx
@@ -0,0 +1,21 @@
+import { MutationConfig } from '../core/types'
+import { getDefaultedMutationConfig } from '../core/config'
+import { useQueryCache } from './ReactQueryCacheProvider'
+import { useContextConfig } from './ReactQueryConfigProvider'
+
+export function useDefaultedMutationConfig<
+  TResult,
+  TError,
+  TVariables,
+  TSnapshot
+>(
+  config?: MutationConfig<TResult, TError, TVariables, TSnapshot>
+): MutationConfig<TResult, TError, TVariables, TSnapshot> {
+  const contextConfig = useContextConfig()
+  const contextQueryCache = useQueryCache()
+  const queryCache = config?.queryCache || contextQueryCache
+  const queryCacheConfig = queryCache.getDefaultConfig()
+  return getDefaultedMutationConfig(queryCacheConfig, contextConfig, config, {
+    queryCache,
+  })
+}
diff --git a/src/react/useDefaultedQueryConfig.tsx b/src/react/useDefaultedQueryConfig.tsx
new file mode 100644
index 000000000..44453b2b7
--- /dev/null
+++ b/src/react/useDefaultedQueryConfig.tsx
@@ -0,0 +1,16 @@
+import { QueryConfig } from '../core/types'
+import { getDefaultedQueryConfig } from '../core/config'
+import { useQueryCache } from './ReactQueryCacheProvider'
+import { useContextConfig } from './ReactQueryConfigProvider'
+
+export function useDefaultedQueryConfig<TResult, TError>(
+  config?: QueryConfig<TResult, TError>
+): QueryConfig<TResult, TError> {
+  const contextConfig = useContextConfig()
+  const contextQueryCache = useQueryCache()
+  const queryCache = config?.queryCache || contextQueryCache
+  const queryCacheConfig = queryCache.getDefaultConfig()
+  return getDefaultedQueryConfig(queryCacheConfig, contextConfig, config, {
+    queryCache,
+  })
+}
diff --git a/src/react/useInfiniteQuery.ts b/src/react/useInfiniteQuery.ts
index 3dec137c4..5689d4c08 100644
--- a/src/react/useInfiniteQuery.ts
+++ b/src/react/useInfiniteQuery.ts
@@ -9,7 +9,7 @@ import {
   TupleQueryFunction,
   TupleQueryKey,
 } from '../core/types'
-import { useQueryArgs } from './useQueryArgs'
+import { getQueryArgs } from '../core/utils'
 
 // TYPES
 
@@ -69,7 +69,6 @@ export function useInfiniteQuery<TResult, TError, TKey extends TupleQueryKey>(
 export function useInfiniteQuery<TResult, TError>(
   ...args: any[]
 ): InfiniteQueryResult<TResult, TError> {
-  let config = useQueryArgs<TResult[], TError>(args)[1]
-  config = { ...config, infinite: true }
-  return useBaseQuery<TResult[], TError>(config)
+  const config = getQueryArgs<TResult[], TError>(args)[1]
+  return useBaseQuery<TResult[], TError>({ ...config, infinite: true })
 }
diff --git a/src/react/useMutation.ts b/src/react/useMutation.ts
index b0606ae04..2a641f2ba 100644
--- a/src/react/useMutation.ts
+++ b/src/react/useMutation.ts
@@ -1,6 +1,6 @@
 import React from 'react'
 
-import { useConfigContext } from './ReactQueryConfigProvider'
+import { useDefaultedMutationConfig } from './useDefaultedMutationConfig'
 import { useGetLatest, useMountedCallback } from './utils'
 import { Console, uid, getStatusProps } from '../core/utils'
 import {
@@ -103,6 +103,9 @@ export function useMutation<
   mutationFn: MutationFunction<TResult, TVariables>,
   config: MutationConfig<TResult, TError, TVariables, TSnapshot> = {}
 ): MutationResultPair<TResult, TError, TVariables, TSnapshot> {
+  config = useDefaultedMutationConfig(config)
+  const getConfig = useGetLatest(config)
+
   const [state, unsafeDispatch] = React.useReducer(
     mutationReducer as Reducer<State<TResult, TError>, Action<TResult, TError>>,
     null,
@@ -113,14 +116,6 @@ export function useMutation<
 
   const getMutationFn = useGetLatest(mutationFn)
 
-  const contextConfig = useConfigContext()
-
-  const getConfig = useGetLatest({
-    ...contextConfig.shared,
-    ...contextConfig.mutations,
-    ...config,
-  })
-
   const latestMutationRef = React.useRef<number>()
 
   const mutate = React.useCallback(
diff --git a/src/react/usePaginatedQuery.ts b/src/react/usePaginatedQuery.ts
index 76048d2dc..806b19b12 100644
--- a/src/react/usePaginatedQuery.ts
+++ b/src/react/usePaginatedQuery.ts
@@ -9,7 +9,7 @@ import {
   TupleQueryFunction,
   TupleQueryKey,
 } from '../core/types'
-import { useQueryArgs } from './useQueryArgs'
+import { getQueryArgs } from '../core/utils'
 
 // A paginated query is more like a "lag" query, which means
 // as the query key changes, we keep the results from the
@@ -74,9 +74,11 @@ export function usePaginatedQuery<TResult, TError, TKey extends TupleQueryKey>(
 export function usePaginatedQuery<TResult, TError>(
   ...args: any[]
 ): PaginatedQueryResult<TResult, TError> {
-  let config = useQueryArgs<TResult, TError>(args)[1]
-  config = { ...config, keepPreviousData: true }
-  const result = useBaseQuery<TResult, TError>(config)
+  const config = getQueryArgs<TResult, TError>(args)[1]
+  const result = useBaseQuery<TResult, TError>({
+    ...config,
+    keepPreviousData: true,
+  })
   return {
     ...result,
     resolvedData: result.data,
diff --git a/src/react/useQuery.ts b/src/react/useQuery.ts
index c0d9fbd33..362268926 100644
--- a/src/react/useQuery.ts
+++ b/src/react/useQuery.ts
@@ -9,7 +9,7 @@ import {
   TupleQueryFunction,
   TupleQueryKey,
 } from '../core/types'
-import { useQueryArgs } from './useQueryArgs'
+import { getQueryArgs } from '../core/utils'
 
 // TYPES
 
@@ -62,6 +62,6 @@ export function useQuery<TResult, TError, TKey extends TupleQueryKey>(
 export function useQuery<TResult, TError>(
   ...args: any[]
 ): QueryResult<TResult, TError> {
-  const config = useQueryArgs<TResult, TError>(args)[1]
+  const config = getQueryArgs<TResult, TError>(args)[1]
   return useBaseQuery<TResult, TError>(config)
 }
diff --git a/src/react/useQueryArgs.ts b/src/react/useQueryArgs.ts
deleted file mode 100644
index 064c2afdb..000000000
--- a/src/react/useQueryArgs.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { getQueryArgs } from '../core/utils'
-import { useConfigContext } from './ReactQueryConfigProvider'
-import { QueryConfig, QueryKey } from '../core/types'
-import { useQueryCache } from './ReactQueryCacheProvider'
-
-export function useQueryArgs<TResult, TError, TOptions = undefined>(
-  args: any[]
-): [QueryKey, QueryConfig<TResult, TError>, TOptions] {
-  const queryCache = useQueryCache()
-
-  const configContext = useConfigContext()
-
-  const [queryKey, config, options] = getQueryArgs<TResult, TError, TOptions>(
-    args
-  )
-
-  // Build the final config
-  const resolvedConfig = {
-    ...configContext.shared,
-    ...configContext.queries,
-    queryCache,
-    ...config,
-  } as QueryConfig<TResult, TError>
-
-  return [queryKey, resolvedConfig, options]
-}


Test patch:
diff --git a/.github/workflows/test-and-publish.yml b/.github/workflows/test-and-publish.yml
index 033c4c6ae..eaffcbf1b 100644
--- a/.github/workflows/test-and-publish.yml
+++ b/.github/workflows/test-and-publish.yml
@@ -39,10 +39,7 @@ jobs:
       - name: Install dependencies
         uses: bahmutov/npm-install@v1
       - run: yarn build
-      - name: Semantic Release
-        uses: cycjimmy/semantic-release-action@v2
-        with:
-          semantic_version: 17
+      - run: npx semantic-release@17
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          NPM_TOKEN: ${{ secrets.npm_token }}
+          NODE_AUTH_TOKEN: ${{secrets.npm_token}}
+          GH_TOKEN: ${{secrets.GH_TOKEN}}
diff --git a/src/react/tests/ReactQueryConfigProvider.test.tsx b/src/react/tests/ReactQueryConfigProvider.test.tsx
index 0c5c3c70d..926ecf1f5 100644
--- a/src/react/tests/ReactQueryConfigProvider.test.tsx
+++ b/src/react/tests/ReactQueryConfigProvider.test.tsx
@@ -42,7 +42,8 @@ describe('ReactQueryConfigProvider', () => {
   })
 
   it('should allow overriding the default config from the outermost provider', async () => {
-    const key = queryKey()
+    const key1 = queryKey()
+    const key2 = queryKey()
 
     const outerConfig = {
       queries: {
@@ -65,25 +66,28 @@ describe('ReactQueryConfigProvider', () => {
     function Container() {
       return (
         <ReactQueryConfigProvider config={outerConfig}>
+          <First />
           <ReactQueryConfigProvider config={innerConfig}>
-            <h1>Placeholder</h1>
+            <Second />
           </ReactQueryConfigProvider>
         </ReactQueryConfigProvider>
       )
     }
 
-    const rendered = render(<Container />)
-
-    await waitFor(() => rendered.getByText('Placeholder'))
-
-    await queryCache.prefetchQuery(key)
+    function First() {
+      const { data } = useQuery(key1)
+      return <span>First: {String(data)}</span>
+    }
 
-    expect(outerConfig.queries.queryFn).toHaveBeenCalledWith(key)
-    expect(innerConfig.queries.queryFn).not.toHaveBeenCalled()
+    function Second() {
+      const { data } = useQuery(key2)
+      return <span>Second: {String(data)}</span>
+    }
 
-    const data = queryCache.getQueryData(key)
+    const rendered = render(<Container />)
 
-    expect(data).toEqual('outer')
+    await waitFor(() => rendered.getByText('First: outer'))
+    await waitFor(() => rendered.getByText('Second: inner'))
   })
 
   it('should reset to defaults when unmounted', async () => {
diff --git a/src/react/tests/useInfiniteQuery.test.tsx b/src/react/tests/useInfiniteQuery.test.tsx
index df837e611..45c3d4c81 100644
--- a/src/react/tests/useInfiniteQuery.test.tsx
+++ b/src/react/tests/useInfiniteQuery.test.tsx
@@ -134,10 +134,10 @@ describe('useInfiniteQuery', () => {
       React.useEffect(() => {
         setTimeout(() => {
           fetchMore()
-        }, 20)
+        }, 50)
         setTimeout(() => {
           setOrder('asc')
-        }, 40)
+        }, 100)
       }, [fetchMore])
 
       return null
diff --git a/src/react/tests/useQuery.test.tsx b/src/react/tests/useQuery.test.tsx
index 6d8e08e58..3837dbb1e 100644
--- a/src/react/tests/useQuery.test.tsx
+++ b/src/react/tests/useQuery.test.tsx
@@ -234,6 +234,48 @@ describe('useQuery', () => {
     })
   })
 
+  // https://github.com/tannerlinsley/react-query/issues/896
+  it('should fetch data in Strict mode when refetchOnMount is false', async () => {
+    const key = queryKey()
+    const states: QueryResult<string>[] = []
+
+    function Page() {
+      const state = useQuery(
+        key,
+        async () => {
+          await sleep(10)
+          return 'test'
+        },
+        {
+          refetchOnMount: false,
+        }
+      )
+      states.push(state)
+      return null
+    }
+
+    render(
+      <React.StrictMode>
+        <Page />
+      </React.StrictMode>
+    )
+
+    await waitFor(() => expect(states.length).toBe(4))
+
+    expect(states[0]).toMatchObject({
+      data: undefined,
+    })
+    expect(states[1]).toMatchObject({
+      data: undefined,
+    })
+    expect(states[2]).toMatchObject({
+      data: 'test',
+    })
+    expect(states[3]).toMatchObject({
+      data: 'test',
+    })
+  })
+
   it('should share equal data structures between query results', async () => {
     const key = queryKey()