Skip to content

Commit c376158

Browse files
authored
feat: add deprecation warnings
* feat: deprecate isDataEqual * feat: deprecate contextSharing * feat: deprecate custom loggers * test: fix ssr-hydration tests we need to compare against actual console.log here (and not our own logger), because react hydration mismatches are also logged to the console if they exist. This has surfaced another issue because of how we import react for react17/18 differential testing. * fix: remove useEffect that would wind up empty in production, and since this is just during development mode, we can just log during render * test: adapt tests we can remove most of these in v5 * chore: fix console.mock declaration * feat: deprecate placeholder data function * chore: scope test execution to src directories if we do `pnpm test` locally, it first does typechecking, which produces output in `build` (including test files). Those will then be tested as well and the svelte tests fail with that. * test: fix vue query test * un-deprecate placeholderData function we might still need it after all - to be discussed * un-deprecate placeholderData function * fix auto import
1 parent e152ea5 commit c376158

File tree

17 files changed

+105
-20
lines changed

17 files changed

+105
-20
lines changed

docs/guides/custom-logger.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ const queryClient = new QueryClient({
2020
},
2121
})
2222
```
23+
24+
**Deprecated**
25+
26+
Custom loggers have been deprecated and will be removed in the next major version.
27+
Logging only has an effect in development mode, where passing a custom logger is not necessary.

docs/guides/placeholder-query-data.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ function Todos() {
2828
}
2929
```
3030

31-
### Placeholder Data as a Function
31+
### Placeholder Data Memoization
3232

33-
If the process for accessing a query's placeholder data is intensive or just not something you want to perform on every render, you can memoize the value or pass a memoized function as the `placeholderData` value:
33+
If the process for accessing a query's placeholder data is intensive or just not something you want to perform on every render, you can memoize the value:
3434

3535
```tsx
3636
function Todos() {

docs/reference/QueryClientProvider.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function App() {
2121
- **Required**
2222
- the QueryClient instance to provide
2323
- `contextSharing: boolean`
24+
- **Deprecated**
2425
- defaults to `false`
2526
- Set this to `true` to enable context sharing, which will share the first and at least one instance of the context across the window to ensure that if React Query is used across different bundles or microfrontends they will all use the same **instance** of context, regardless of module scoping.
2627
- `context?: React.Context<QueryClient | undefined>`

docs/reference/useQuery.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const {
3636
networkMode,
3737
initialData,
3838
initialDataUpdatedAt,
39-
isDataEqual,
4039
keepPreviousData,
4140
meta,
4241
notifyOnChangeProps,
@@ -171,6 +170,8 @@ const {
171170
- Defaults to `false`
172171
- If set, any previous `data` will be kept when fetching new data because the query key changed.
173172
- `isDataEqual: (oldData: TData | undefined, newData: TData) => boolean`
173+
- **Deprecated**. You can achieve the same functionality by passing a function to `structuralSharing` instead:
174+
- structuralSharing: (oldData, newData) => isDataEqual(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData)
174175
- Optional
175176
- This function should return boolean indicating whether to use previous `data` (`true`) or new data (`false`) as a resolved data for the query.
176177
- `structuralSharing: boolean | ((oldData: TData | undefined, newData: TData) => TData)`

jest-preset.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const moduleNameMapper = {
2424
module.exports = {
2525
collectCoverage: true,
2626
coverageReporters: ['json', 'lcov', 'text', 'clover', 'text-summary'],
27-
testMatch: ['<rootDir>/**/*.test.[jt]s?(x)'],
27+
testMatch: ['<rootDir>/**/src/**/*.test.[jt]s?(x)'],
2828
transform: { '^.+\\.(ts|tsx)$': 'ts-jest' },
2929
clearMocks: true,
3030
testEnvironment: 'jsdom',

packages/query-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export { focusManager } from './focusManager'
1313
export { onlineManager } from './onlineManager'
1414
export {
1515
hashQueryKey,
16+
replaceEqualDeep,
1617
isError,
1718
isServer,
1819
parseQueryArgs,

packages/query-core/src/queryClient.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ export class QueryClient {
7171
this.defaultOptions = config.defaultOptions || {}
7272
this.queryDefaults = []
7373
this.mutationDefaults = []
74+
75+
if (process.env.NODE_ENV !== 'production' && config.logger) {
76+
this.logger.error(
77+
`Passing a custom logger has been deprecated and will be removed in the next major version.`,
78+
)
79+
}
7480
}
7581

7682
mount(): void {

packages/query-core/src/queryObserver.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ export class QueryObserver<
155155

156156
this.options = this.client.defaultQueryOptions(options)
157157

158+
if (
159+
process.env.NODE_ENV !== 'production' &&
160+
typeof options?.isDataEqual !== 'undefined'
161+
) {
162+
this.client
163+
.getLogger()
164+
.error(
165+
`The isDataEqual option has been deprecated and will be removed in the next major version. You can achieve the same functionality by passing a function as the structuralSharing option`,
166+
)
167+
}
168+
158169
if (!shallowEqualObjects(prevOptions, this.options)) {
159170
this.client.getQueryCache().notify({
160171
type: 'observerOptionsUpdated',

packages/query-core/src/tests/queriesObserver.test.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ describe('queriesObserver', () => {
5050
unsubscribe()
5151
expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
5252

53-
expect(mockLogger.error).toHaveBeenCalledTimes(1)
53+
expect(mockLogger.error).toHaveBeenCalledTimes(2)
54+
expect(mockLogger.error).toHaveBeenCalledWith(
55+
'Passing a custom logger has been deprecated and will be removed in the next major version.',
56+
)
57+
expect(mockLogger.error).toHaveBeenCalledWith(
58+
'Passing a custom logger has been deprecated and will be removed in the next major version.',
59+
)
5460
})
5561

5662
test('should update when a query updates', async () => {

packages/query-core/src/tests/queryClient.test.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ describe('queryClient', () => {
160160
// No defaults, no warning
161161
const noDefaults = queryClient.getQueryDefaults(keyABCD)
162162
expect(noDefaults).toBeUndefined()
163-
expect(mockLogger.error).not.toHaveBeenCalled()
163+
expect(mockLogger.error).toHaveBeenCalledTimes(1)
164164

165165
// If defaults for key ABCD are registered **before** the ones of key ABC (more generic)…
166166
queryClient.setQueryDefaults(keyABCD, defaultsOfABCD)
@@ -169,7 +169,7 @@ describe('queryClient', () => {
169169
const goodDefaults = queryClient.getQueryDefaults(keyABCD)
170170
expect(goodDefaults).toBe(defaultsOfABCD)
171171
// The warning is still raised since several defaults are matching
172-
expect(mockLogger.error).toHaveBeenCalledTimes(1)
172+
expect(mockLogger.error).toHaveBeenCalledTimes(2)
173173

174174
// Let's create another queryClient and change the order of registration
175175
const newQueryClient = createQueryClient()
@@ -180,7 +180,7 @@ describe('queryClient', () => {
180180
const badDefaults = newQueryClient.getQueryDefaults(keyABCD)
181181
expect(badDefaults).not.toBe(defaultsOfABCD)
182182
expect(badDefaults).toBe(defaultsOfABC)
183-
expect(mockLogger.error).toHaveBeenCalledTimes(2)
183+
expect(mockLogger.error).toHaveBeenCalledTimes(4)
184184
})
185185

186186
test('should warn in dev if several mutation defaults match a given key', () => {
@@ -216,7 +216,10 @@ describe('queryClient', () => {
216216
// No defaults, no warning
217217
const noDefaults = queryClient.getMutationDefaults(keyABCD)
218218
expect(noDefaults).toBeUndefined()
219-
expect(mockLogger.error).not.toHaveBeenCalled()
219+
expect(mockLogger.error).toHaveBeenNthCalledWith(
220+
1,
221+
'Passing a custom logger has been deprecated and will be removed in the next major version.',
222+
)
220223

221224
// If defaults for key ABCD are registered **before** the ones of key ABC (more generic)…
222225
queryClient.setMutationDefaults(keyABCD, defaultsOfABCD)
@@ -225,7 +228,7 @@ describe('queryClient', () => {
225228
const goodDefaults = queryClient.getMutationDefaults(keyABCD)
226229
expect(goodDefaults).toBe(defaultsOfABCD)
227230
// The warning is still raised since several defaults are matching
228-
expect(mockLogger.error).toHaveBeenCalledTimes(1)
231+
expect(mockLogger.error).toHaveBeenCalledTimes(2)
229232

230233
// Let's create another queryClient and change the order of registration
231234
const newQueryClient = createQueryClient()
@@ -236,7 +239,7 @@ describe('queryClient', () => {
236239
const badDefaults = newQueryClient.getMutationDefaults(keyABCD)
237240
expect(badDefaults).not.toBe(defaultsOfABCD)
238241
expect(badDefaults).toBe(defaultsOfABC)
239-
expect(mockLogger.error).toHaveBeenCalledTimes(2)
242+
expect(mockLogger.error).toHaveBeenCalledTimes(4)
240243
})
241244
})
242245

packages/query-core/src/tests/queryObserver.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ describe('queryObserver', () => {
690690
},
691691
})
692692

693-
expect(mockLogger.error).toHaveBeenNthCalledWith(1, new Error('error'))
693+
expect(mockLogger.error).toHaveBeenNthCalledWith(2, new Error('error'))
694694
})
695695

696696
test('should not use replaceEqualDeep for select value when structuralSharing option is true and placeholderdata is defined', () => {

packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,8 @@ describe('PersistQueryClientProvider', () => {
416416

417417
await waitFor(() => rendered.getByText('fetched'))
418418
expect(removeClient).toHaveBeenCalledTimes(1)
419-
expect(mockLogger.error).toHaveBeenCalledTimes(1)
420-
expect(mockLogger.error).toHaveBeenCalledWith(error)
419+
expect(mockLogger.error).toHaveBeenCalledTimes(2)
420+
expect(mockLogger.error).toHaveBeenNthCalledWith(2, error)
421421
})
422422

423423
test('should be able to persist into multiple clients', async () => {

packages/react-query/src/QueryClientProvider.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ export const QueryClientProvider = ({
8080
}
8181
}, [client])
8282

83+
if (process.env.NODE_ENV !== 'production' && contextSharing) {
84+
client
85+
.getLogger()
86+
.error(
87+
`The contextSharing option has been deprecated and will be removed in the next major version`,
88+
)
89+
}
90+
8391
const Context = getQueryClientContext(context, contextSharing)
8492

8593
return (

packages/react-query/src/__tests__/ssr-hydration.test.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
dehydrate,
1313
hydrate,
1414
} from '..'
15-
import { createQueryClient, mockLogger, setIsServer, sleep } from './utils'
15+
import { createQueryClient, setIsServer, sleep } from './utils'
1616

1717
const isReact18 = () => (process.env.REACTJS_VERSION || '18') === '18'
1818

@@ -55,6 +55,9 @@ describe('Server side rendering with de/rehydration', () => {
5555
globalThis.IS_REACT_ACT_ENVIRONMENT = previousIsReactActEnvironment
5656
})
5757
it('should not mismatch on success', async () => {
58+
const consoleMock = jest.spyOn(console, 'error')
59+
consoleMock.mockImplementation(() => undefined)
60+
5861
if (!isReact18()) {
5962
return
6063
}
@@ -118,15 +121,23 @@ describe('Server side rendering with de/rehydration', () => {
118121
)
119122

120123
// Check that we have no React hydration mismatches
121-
expect(mockLogger.error).not.toHaveBeenCalled()
124+
// this should be zero calls and can be changed once we drop react17 support
125+
expect(consoleMock).toHaveBeenNthCalledWith(
126+
1,
127+
'Warning: You are importing hydrateRoot from "react-dom" which is not supported. You should instead import it from "react-dom/client".',
128+
)
122129
expect(fetchDataSuccess).toHaveBeenCalledTimes(2)
123130
expect(el.innerHTML).toBe(expectedMarkup)
124131

125132
unmount()
126133
queryClient.clear()
134+
consoleMock.mockRestore()
127135
})
128136

129137
it('should not mismatch on error', async () => {
138+
const consoleMock = jest.spyOn(console, 'error')
139+
consoleMock.mockImplementation(() => undefined)
140+
130141
if (!isReact18()) {
131142
return
132143
}
@@ -187,7 +198,7 @@ describe('Server side rendering with de/rehydration', () => {
187198
)
188199

189200
// We expect exactly one console.error here, which is from the
190-
expect(mockLogger.error).toHaveBeenCalledTimes(1)
201+
expect(consoleMock).toHaveBeenCalledTimes(1)
191202
expect(fetchDataError).toHaveBeenCalledTimes(2)
192203
expect(el.innerHTML).toBe(expectedMarkup)
193204
await sleep(50)
@@ -198,9 +209,13 @@ describe('Server side rendering with de/rehydration', () => {
198209

199210
unmount()
200211
queryClient.clear()
212+
consoleMock.mockRestore()
201213
})
202214

203215
it('should not mismatch on queries that were not prefetched', async () => {
216+
const consoleMock = jest.spyOn(console, 'error')
217+
consoleMock.mockImplementation(() => undefined)
218+
204219
if (!isReact18()) {
205220
return
206221
}
@@ -254,7 +269,11 @@ describe('Server side rendering with de/rehydration', () => {
254269
)
255270

256271
// Check that we have no React hydration mismatches
257-
expect(mockLogger.error).not.toHaveBeenCalled()
272+
// this should be zero calls and can be changed once we drop react17 support
273+
expect(consoleMock).toHaveBeenNthCalledWith(
274+
1,
275+
'Warning: You are importing hydrateRoot from "react-dom" which is not supported. You should instead import it from "react-dom/client".',
276+
)
258277
expect(fetchDataSuccess).toHaveBeenCalledTimes(1)
259278
expect(el.innerHTML).toBe(expectedMarkup)
260279
await sleep(50)
@@ -265,5 +284,6 @@ describe('Server side rendering with de/rehydration', () => {
265284

266285
unmount()
267286
queryClient.clear()
287+
consoleMock.mockRestore()
268288
})
269289
})

packages/solid-query/src/QueryClientProvider.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,17 @@ export const QueryClientProvider = (
8080
},
8181
props,
8282
)
83-
onMount(() => mergedProps.client.mount())
83+
onMount(() => {
84+
mergedProps.client.mount()
85+
86+
if (process.env.NODE_ENV !== 'production' && mergedProps.contextSharing) {
87+
mergedProps.client
88+
.getLogger()
89+
.error(
90+
`The contextSharing option has been deprecated and will be removed in the next major version`,
91+
)
92+
}
93+
})
8494
onCleanup(() => mergedProps.client.unmount())
8595

8696
const QueryClientContext = getQueryClientContext(

packages/vue-query/src/__tests__/vueQueryPlugin.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,12 @@ describe('VueQueryPlugin', () => {
245245
})
246246

247247
test('should use existing context', () => {
248-
const customClient = { mount: jest.fn() } as unknown as QueryClient
248+
const customClient = {
249+
mount: jest.fn(),
250+
getLogger: () => ({
251+
error: jest.fn(),
252+
}),
253+
} as unknown as QueryClient
249254
window.__VUE_QUERY_CONTEXT__ = customClient
250255
const appMock = getAppMock()
251256
VueQueryPlugin.install(appMock, { contextSharing: true })

packages/vue-query/src/vueQueryPlugin.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ export const VueQueryPlugin = {
5959

6060
client.mount()
6161

62+
if (process.env.NODE_ENV !== 'production' && options.contextSharing) {
63+
client
64+
.getLogger()
65+
.error(
66+
`The contextSharing option has been deprecated and will be removed in the next major version`,
67+
)
68+
}
69+
6270
const cleanup = () => {
6371
client.unmount()
6472
}

0 commit comments

Comments
 (0)