From cfb15ef582e080c82c149cdcba998d3d5e570ae7 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 23 Dec 2022 08:38:57 +0100 Subject: [PATCH 001/314] docs: migration guide --- docs/config.json | 4 ++++ docs/react/guides/migrating-to-react-query-5.md | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 docs/react/guides/migrating-to-react-query-5.md diff --git a/docs/config.json b/docs/config.json index 0bee2e6155..8a4f92ff42 100644 --- a/docs/config.json +++ b/docs/config.json @@ -183,6 +183,10 @@ { "label": "Migrating to React Query 4", "to": "react/guides/migrating-to-react-query-4" + }, + { + "label": "Migrating to React Query 5", + "to": "react/guides/migrating-to-react-query-5" } ] }, diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md new file mode 100644 index 0000000000..2c38462536 --- /dev/null +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -0,0 +1,9 @@ +--- +id: migrating-to-react-query-5 +title: Migrating to TanStack Query v5 +--- + +## Breaking Changes + +v5 is a major version, so there are some breaking changes to be aware of: + From 651610b0c86f373371b611a01709ee41f6ac3db1 Mon Sep 17 00:00:00 2001 From: "Mahmoud M. Anwar" Date: Sun, 25 Dec 2022 20:27:21 +0200 Subject: [PATCH 002/314] feat: remove `remove` returned from useQuery (#4702) * refactor(useQuery): remove "remove" returned from useQuery * test(useQuery): remove tests which are using "remove" returned from useQuery * docs(useQuery): remove "remove" method from useQuery documentation * refactor(useInfiniteQuery): remove "remove" returned from useInfiniteQuery * test(useInfiniteQuery): remove tests which use "remove" method from useInfiniteQuery * refactor(queryObserver): remove "remove" method from queryObserver * test(useQuery): replace remove method from useQuery by queryClient.removeQueries * docs(migration-v5): document removing `remove` method Co-authored-by: Dominik Dorfmeister BREAKING CHANGE: the remove method returned from useQuery is no longer available. --- .../guides/migrating-to-react-query-5.md | 15 ++ docs/react/reference/useQuery.md | 3 - packages/query-core/src/queryObserver.ts | 6 - packages/query-core/src/types.ts | 1 - .../src/__tests__/useInfiniteQuery.test.tsx | 2 - .../src/__tests__/useQuery.test.tsx | 19 +- .../__tests__/createInfiniteQuery.test.tsx | 2 - .../src/__tests__/createQuery.test.tsx | 167 ------------------ packages/vue-query/src/useInfiniteQuery.ts | 4 +- packages/vue-query/src/useQuery.ts | 7 +- 10 files changed, 26 insertions(+), 200 deletions(-) diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index 2c38462536..9b1bc12182 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -7,3 +7,18 @@ title: Migrating to TanStack Query v5 v5 is a major version, so there are some breaking changes to be aware of: +### The `remove` method has been removed from useQuery + +Previously, remove method used to remove the query from the queryCache without informing observers about it. It was best used to remove data imperatively that is no longer needed, e.g. when logging a user out. + +But It doesn't make much sense to do this while a query is still active, because it will just trigger a hard loading state with the next re-render. + +if you still need to remove a query, you can use `queryClient.removeQueries({queryKey: key})` + +```diff + const queryClient = useQueryClient(); + const query = useQuery({ queryKey, queryFn }); + +- query.remove() ++ queryClient.removeQueries({ queryKey }) +``` diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index 0396fe7087..2531bf39a6 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -25,7 +25,6 @@ const { isStale, isSuccess, refetch, - remove, status, fetchStatus, } = useQuery({ @@ -259,5 +258,3 @@ const { - Defaults to `true` - Per default, a currently running request will be cancelled before a new request is made - When set to `false`, no refetch will be made if there is already a request running. -- `remove: () => void` - - A function to remove the query from the cache. diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index d23850bcc8..9e4b85634c 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -95,7 +95,6 @@ export class QueryObserver< } protected bindMethods(): void { - this.remove = this.remove.bind(this) this.refetch = this.refetch.bind(this) } @@ -270,10 +269,6 @@ export class QueryObserver< return this.currentQuery } - remove(): void { - this.client.getQueryCache().remove(this.currentQuery) - } - refetch({ refetchPage, ...options @@ -583,7 +578,6 @@ export class QueryObserver< isRefetchError: isError && state.dataUpdatedAt !== 0, isStale: isStale(query, options), refetch: this.refetch, - remove: this.remove, } return result as QueryObserverResult diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index b978366743..472593801b 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -390,7 +390,6 @@ export interface QueryObserverBaseResult { refetch: ( options?: RefetchOptions & RefetchQueryFilters, ) => Promise> - remove: () => void status: QueryStatus fetchStatus: FetchStatus } diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx index 05c45ed736..c6cdabd102 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx @@ -93,7 +93,6 @@ describe('useInfiniteQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -127,7 +126,6 @@ describe('useInfiniteQuery', () => { isStale: true, isSuccess: true, refetch: expect.any(Function), - remove: expect.any(Function), status: 'success', fetchStatus: 'idle', }) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 855a817ec4..a2598f7577 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -250,7 +250,6 @@ describe('useQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -278,7 +277,6 @@ describe('useQuery', () => { isStale: true, isSuccess: true, refetch: expect.any(Function), - remove: expect.any(Function), status: 'success', fetchStatus: 'idle', }) @@ -337,7 +335,6 @@ describe('useQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -365,7 +362,6 @@ describe('useQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -393,7 +389,6 @@ describe('useQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'error', fetchStatus: 'idle', }) @@ -889,7 +884,7 @@ describe('useQuery', () => { + data: {state.data ?? 'null'} @@ -1260,11 +1255,13 @@ describe('useQuery', () => { states.push(state) - const { remove, refetch } = state + const { refetch } = state return (
- + data: {state.data ?? 'null'}
diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx index 0c3802d840..619cfe2238 100644 --- a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -103,7 +103,6 @@ describe('useInfiniteQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -137,7 +136,6 @@ describe('useInfiniteQuery', () => { isStale: true, isSuccess: true, refetch: expect.any(Function), - remove: expect.any(Function), status: 'success', fetchStatus: 'idle', }) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 21b80ad554..47e0ada36e 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -283,7 +283,6 @@ describe('createQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -311,7 +310,6 @@ describe('createQuery', () => { isStale: true, isSuccess: true, refetch: expect.any(Function), - remove: expect.any(Function), status: 'success', fetchStatus: 'idle', }) @@ -376,7 +374,6 @@ describe('createQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -404,7 +401,6 @@ describe('createQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'loading', fetchStatus: 'fetching', }) @@ -432,7 +428,6 @@ describe('createQuery', () => { isStale: true, isSuccess: false, refetch: expect.any(Function), - remove: expect.any(Function), status: 'error', fetchStatus: 'idle', }) @@ -973,64 +968,6 @@ describe('createQuery', () => { }) }) - it.skip('should not get into an infinite loop when removing a query with cacheTime 0 and rerendering', async () => { - const key = queryKey() - const states: CreateQueryResult[] = [] - - function Page() { - //@ts-expect-error -- skip this test - const [, rerender] = NotReact.useState({}) - - const state = createQuery( - key, - async () => { - await sleep(5) - return 'data' - }, - { - cacheTime: 0, - notifyOnChangeProps: 'all', - }, - ) - - createRenderEffect(() => { - states.push({ ...state }) - }) - - const { remove } = state - - //@ts-expect-error skip this test - NotReact.useEffect(() => { - setActTimeout(() => { - remove() - rerender({}) - }, 20) - }, [remove]) - - return null - } - - render(() => ( - - - - )) - - await sleep(100) - - expect(states.length).toBe(5) - // First load - expect(states[0]).toMatchObject({ isLoading: true, isSuccess: false }) - // First success - expect(states[1]).toMatchObject({ isLoading: false, isSuccess: true }) - // Remove - expect(states[2]).toMatchObject({ isLoading: true, isSuccess: false }) - // Hook state update - expect(states[3]).toMatchObject({ isLoading: true, isSuccess: false }) - // Second success - expect(states[4]).toMatchObject({ isLoading: false, isSuccess: true }) - }) - it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -1339,112 +1276,8 @@ describe('createQuery', () => { expect(states[1]).toMatchObject({ data: 'test' }) }) - it.skip('should be able to remove a query', async () => { - const key = queryKey() - const states: CreateQueryResult[] = [] - let count = 0 - - function Page() { - //@ts-expect-error -- we skip this test, and no such thing as rerender in solid - const [, rerender] = NotReact.useState({}) - const state = createQuery(key, () => ++count, { - notifyOnChangeProps: 'all', - }) - - createRenderEffect(() => { - states.push({ ...state }) - }) - - const { remove } = state - - return ( -
- - - data: {state.data ?? 'null'} -
- ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data: 1')) - fireEvent.click(screen.getByRole('button', { name: /remove/i })) - - await sleep(20) - fireEvent.click(screen.getByRole('button', { name: /rerender/i })) - await waitFor(() => screen.getByText('data: 2')) - - expect(states.length).toBe(4) - // Initial - expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) - // Fetched - expect(states[1]).toMatchObject({ status: 'success', data: 1 }) - // Remove + Hook state update, batched - expect(states[2]).toMatchObject({ status: 'loading', data: undefined }) - // Fetched - expect(states[3]).toMatchObject({ status: 'success', data: 2 }) - }) - - it('should create a new query when refetching a removed query', async () => { - const key = queryKey() - const states: CreateQueryResult[] = [] - let count = 0 - - function Page() { - const state = createQuery( - key, - async () => { - await sleep(10) - return ++count - }, - { notifyOnChangeProps: 'all' }, - ) - - createRenderEffect(() => { - states.push({ ...state }) - }) - - return ( -
- - - data: {state.data ?? 'null'} -
- ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data: 1')) - fireEvent.click(screen.getByRole('button', { name: /remove/i })) - - await sleep(50) - fireEvent.click(screen.getByRole('button', { name: /refetch/i })) - await waitFor(() => screen.getByText('data: 2')) - - expect(states.length).toBe(4) - // Initial - expect(states[0]).toMatchObject({ data: undefined, dataUpdatedAt: 0 }) - // Fetched - expect(states[1]).toMatchObject({ data: 1 }) - // Switch - expect(states[2]).toMatchObject({ data: undefined, dataUpdatedAt: 0 }) - // Fetched - expect(states[3]).toMatchObject({ data: 2 }) - }) - it('should share equal data structures between query results', async () => { const key = queryKey() - const result1 = [ { id: '1', done: false }, { id: '2', done: false }, diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index 6b55c53c04..ed4b1fd9c5 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -38,7 +38,7 @@ type InfiniteQueryReturnType = UseQueryReturnType< > export type UseInfiniteQueryReturnType = DistributiveOmit< InfiniteQueryReturnType, - 'fetchNextPage' | 'fetchPreviousPage' | 'refetch' | 'remove' + 'fetchNextPage' | 'fetchPreviousPage' | 'refetch' > & { fetchNextPage: InfiniteQueryObserverResult['fetchNextPage'] fetchPreviousPage: InfiniteQueryObserverResult< @@ -46,7 +46,6 @@ export type UseInfiniteQueryReturnType = DistributiveOmit< TError >['fetchPreviousPage'] refetch: InfiniteQueryObserverResult['refetch'] - remove: InfiniteQueryObserverResult['remove'] } export function useInfiniteQuery< @@ -110,6 +109,5 @@ export function useInfiniteQuery< fetchNextPage: result.fetchNextPage.value, fetchPreviousPage: result.fetchPreviousPage.value, refetch: result.refetch.value, - remove: result.remove.value, } } diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index a63ab007ad..fa11af87de 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -16,19 +16,17 @@ import type { export type UseQueryReturnType = DistributiveOmit< UQRT, - 'refetch' | 'remove' + 'refetch' > & { refetch: QueryObserverResult['refetch'] - remove: QueryObserverResult['remove'] } export type UseQueryDefinedReturnType = DistributiveOmit< ToRefs>>, - 'refetch' | 'remove' + 'refetch' > & { suspense: () => Promise> refetch: QueryObserverResult['refetch'] - remove: QueryObserverResult['remove'] } export type UseQueryOptions< @@ -173,6 +171,5 @@ export function useQuery< return { ...result, refetch: result.refetch.value, - remove: result.remove.value, } } From be1ec4623c15493ad281d35e7a4ba9d1594da4eb Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 27 Dec 2022 21:37:56 +0100 Subject: [PATCH 003/314] feat: ts 4.7 (#4708) * chore: use typescript 4.7.4 everywhere * docs: mention v4.7 * fix(vue-query): use proper return type in useBaseQuery * fix(solid-query): Correct conditional type check Correct conditional typing when data is `undefined` in createBaseQuery Co-authored-by: Damian Osipiuk Co-authored-by: Aryan Deora --- .../guides/migrating-to-react-query-5.md | 6 +- docs/react/typescript.md | 2 +- examples/react/basic-typescript/package.json | 2 +- .../package.json | 2 +- .../solid/basic-graphql-request/package.json | 2 +- examples/solid/basic-typescript/package.json | 2 +- .../solid/default-query-function/package.json | 2 +- examples/solid/simple/package.json | 2 +- examples/vue/basic/package.json | 2 +- package.json | 2 +- packages/solid-query/src/createBaseQuery.ts | 2 +- packages/vue-query/src/useBaseQuery.ts | 4 +- pnpm-lock.yaml | 156 +++++++++--------- 13 files changed, 96 insertions(+), 90 deletions(-) diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index 9b1bc12182..dee7d8b90a 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -11,7 +11,7 @@ v5 is a major version, so there are some breaking changes to be aware of: Previously, remove method used to remove the query from the queryCache without informing observers about it. It was best used to remove data imperatively that is no longer needed, e.g. when logging a user out. -But It doesn't make much sense to do this while a query is still active, because it will just trigger a hard loading state with the next re-render. +But It doesn't make much sense to do this while a query is still active, because it will just trigger a hard loading state with the next re-render. if you still need to remove a query, you can use `queryClient.removeQueries({queryKey: key})` @@ -22,3 +22,7 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que - query.remove() + queryClient.removeQueries({ queryKey }) ``` + +### The minimum required TypeScript version is now 4.7 + +Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. diff --git a/docs/react/typescript.md b/docs/react/typescript.md index 6b67741cd8..7fb8358c42 100644 --- a/docs/react/typescript.md +++ b/docs/react/typescript.md @@ -7,7 +7,7 @@ React Query is now written in **TypeScript** to make sure the library and your p Things to keep in mind: -- Types currently require using TypeScript v4.1 or greater +- Types currently require using TypeScript **v4.7** or greater - Changes to types in this repository are considered **non-breaking** and are usually released as **patch** semver changes (otherwise every type enhancement would be a major version!). - It is **highly recommended that you lock your react-query package version to a specific patch release and upgrade with the expectation that types may be fixed or upgraded between any release** - The non-type-related public API of React Query still follows semver very strictly. diff --git a/examples/react/basic-typescript/package.json b/examples/react/basic-typescript/package.json index d48c11c2bd..7a27ef29d2 100644 --- a/examples/react/basic-typescript/package.json +++ b/examples/react/basic-typescript/package.json @@ -23,7 +23,7 @@ "@vitejs/plugin-react": "^2.0.0", "eslint": "7.x", "eslint-config-prettier": "^8.3.0", - "typescript": "^4.2.3", + "typescript": "4.7.4", "vite": "^3.0.0" }, "browserslist": { diff --git a/examples/react/optimistic-updates-typescript/package.json b/examples/react/optimistic-updates-typescript/package.json index a3e04ff865..620e576a53 100755 --- a/examples/react/optimistic-updates-typescript/package.json +++ b/examples/react/optimistic-updates-typescript/package.json @@ -13,7 +13,7 @@ "react-dom": "^18.2.0", "@tanstack/react-query": "^4.7.1", "@tanstack/react-query-devtools": "^4.7.1", - "typescript": "^4.1.2" + "typescript": "4.7.4" }, "scripts": { "dev": "next", diff --git a/examples/solid/basic-graphql-request/package.json b/examples/solid/basic-graphql-request/package.json index 03d0f452ab..476ca6767c 100644 --- a/examples/solid/basic-graphql-request/package.json +++ b/examples/solid/basic-graphql-request/package.json @@ -16,7 +16,7 @@ "solid-js": "^1.5.1" }, "devDependencies": { - "typescript": "^4.8.2", + "typescript": "4.7.4", "vite": "^3.0.9", "vite-plugin-solid": "^2.3.9" } diff --git a/examples/solid/basic-typescript/package.json b/examples/solid/basic-typescript/package.json index 0e265cf44c..5437cdd79e 100644 --- a/examples/solid/basic-typescript/package.json +++ b/examples/solid/basic-typescript/package.json @@ -14,7 +14,7 @@ "solid-js": "^1.5.1" }, "devDependencies": { - "typescript": "^4.8.2", + "typescript": "4.7.4", "vite": "^3.0.9", "vite-plugin-solid": "^2.3.9" } diff --git a/examples/solid/default-query-function/package.json b/examples/solid/default-query-function/package.json index 88274cdb4c..306da8eb14 100644 --- a/examples/solid/default-query-function/package.json +++ b/examples/solid/default-query-function/package.json @@ -14,7 +14,7 @@ "solid-js": "^1.5.1" }, "devDependencies": { - "typescript": "^4.8.2", + "typescript": "4.7.4", "vite": "^3.0.9", "vite-plugin-solid": "^2.3.9" } diff --git a/examples/solid/simple/package.json b/examples/solid/simple/package.json index 8d777ca6f0..8f5cbf07cf 100644 --- a/examples/solid/simple/package.json +++ b/examples/solid/simple/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@tanstack/eslint-plugin-query": "^4.13.0", - "typescript": "^4.8.2", + "typescript": "4.7.4", "vite": "^3.0.9", "vite-plugin-solid": "^2.3.9" } diff --git a/examples/vue/basic/package.json b/examples/vue/basic/package.json index 8c5aac130a..0673943888 100644 --- a/examples/vue/basic/package.json +++ b/examples/vue/basic/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^3.1.2", - "typescript": "^4.8.4", + "typescript": "4.7.4", "vite": "^3.1.8" } } diff --git a/package.json b/package.json index 6bf590c152..57a69ce181 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "svelte": "^3.48.0", "ts-jest": "^27.1.1", "ts-node": "^10.7.0", - "typescript": "^4.7.4", + "typescript": "4.7.4", "vue": "^3.2.33" }, "bundlewatch": { diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts index 209c70532c..de4c96d6fd 100644 --- a/packages/solid-query/src/createBaseQuery.ts +++ b/packages/solid-query/src/createBaseQuery.ts @@ -45,7 +45,7 @@ export function createBaseQuery< () => { return new Promise((resolve) => { if (!(state.isFetching && state.isLoading)) { - if (unwrap(state.data) === emptyData) { + if ((unwrap(state.data) as TData | typeof emptyData) === emptyData) { resolve(undefined) } resolve(unwrap(state.data)) diff --git a/packages/vue-query/src/useBaseQuery.ts b/packages/vue-query/src/useBaseQuery.ts index caa7173d96..fb133f7074 100644 --- a/packages/vue-query/src/useBaseQuery.ts +++ b/packages/vue-query/src/useBaseQuery.ts @@ -129,7 +129,9 @@ export function useBaseQuery< } return { - ...(toRefs(readonly(state)) as UseQueryReturnType), + ...(toRefs(readonly(state)) as ToRefs< + Readonly> + >), suspense, } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a84457bef4..9dfbabb119 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,7 +70,7 @@ importers: svelte: ^3.48.0 ts-jest: ^27.1.1 ts-node: ^10.7.0 - typescript: ^4.7.4 + typescript: 4.7.4 vue: ^3.2.33 devDependencies: '@babel/core': 7.19.1 @@ -94,8 +94,8 @@ importers: '@types/react': 18.0.15 '@types/react-dom': 18.0.6 '@types/testing-library__jest-dom': 5.14.5 - '@typescript-eslint/eslint-plugin': 5.41.0_b2esjfxatoxyearpbj3ml2e4hy - '@typescript-eslint/parser': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/eslint-plugin': 5.41.0_i2aae4mvohkbme6pfmscna5hqe + '@typescript-eslint/parser': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu axios: 0.26.1 babel-eslint: 10.1.0_eslint@7.32.0 babel-jest: 27.5.1_@babel+core@7.19.1 @@ -106,7 +106,7 @@ importers: current-git-branch: 1.1.0 eslint: 7.32.0 eslint-config-prettier: 6.15.0_eslint@7.32.0 - eslint-config-react-app: 5.2.1_g3icqb772h4a27ny4eeob5pf34 + eslint-config-react-app: 5.2.1_kl5dmxelusuapydtgrn7w3jwbq eslint-config-standard: 14.1.1_tkxmwkr7odbap4fwlt52e6z7au eslint-config-standard-react: 9.2.0_c3l3lp52boouk5zx5rvypilpme eslint-import-resolver-typescript: 2.7.1_hpmu7kn6tcn2vnxpfzvv33bxmy @@ -137,9 +137,9 @@ importers: solid-testing-library: 0.3.0_solid-js@1.5.7 stream-to-array: 2.3.0 svelte: 3.49.0 - ts-jest: 27.1.5_kv3z5ukkp6hjlassekz7vtidta - ts-node: 10.8.2_rb7lfb2dlgdf5f7m6mcvvespxa - typescript: 4.8.3 + ts-jest: 27.1.5_f5rbw76fdbj6kcejdlin5zoydm + ts-node: 10.8.2_x2utdhayajzrh747hktprshhby + typescript: 4.7.4 vue: 3.2.37 examples/react/auto-refetching: @@ -217,7 +217,7 @@ importers: eslint-config-prettier: ^8.3.0 react: ^18.0.0 react-dom: ^18.0.0 - typescript: ^4.2.3 + typescript: 4.7.4 vite: ^3.0.0 dependencies: '@tanstack/query-sync-storage-persister': link:../../../packages/query-sync-storage-persister @@ -234,7 +234,7 @@ importers: '@vitejs/plugin-react': 2.1.0_vite@3.1.4 eslint: 7.32.0 eslint-config-prettier: 8.5.0_eslint@7.32.0 - typescript: 4.8.3 + typescript: 4.7.4 vite: 3.1.4 examples/react/default-query-function: @@ -338,7 +338,7 @@ importers: next: 12.2.2 react: ^18.2.0 react-dom: ^18.2.0 - typescript: ^4.1.2 + typescript: 4.7.4 dependencies: '@tanstack/react-query': link:../../../packages/react-query '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools @@ -349,7 +349,7 @@ importers: next: 12.2.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - typescript: 4.8.3 + typescript: 4.7.4 examples/react/pagination: specifiers: @@ -582,7 +582,7 @@ importers: graphql: ^16.6.0 graphql-request: ^5.0.0 solid-js: ^1.5.1 - typescript: ^4.8.2 + typescript: 4.7.4 vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: @@ -591,7 +591,7 @@ importers: graphql-request: 5.0.0_graphql@16.6.0 solid-js: 1.5.4 devDependencies: - typescript: 4.8.3 + typescript: 4.7.4 vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 @@ -599,14 +599,14 @@ importers: specifiers: '@tanstack/solid-query': ^4.3.9 solid-js: ^1.5.1 - typescript: ^4.8.2 + typescript: 4.7.4 vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: '@tanstack/solid-query': link:../../../packages/solid-query solid-js: 1.5.4 devDependencies: - typescript: 4.8.3 + typescript: 4.7.4 vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 @@ -614,14 +614,14 @@ importers: specifiers: '@tanstack/solid-query': ^4.3.9 solid-js: ^1.5.1 - typescript: ^4.8.2 + typescript: 4.7.4 vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: '@tanstack/solid-query': link:../../../packages/solid-query solid-js: 1.5.4 devDependencies: - typescript: 4.8.3 + typescript: 4.7.4 vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 @@ -630,7 +630,7 @@ importers: '@tanstack/eslint-plugin-query': ^4.13.0 '@tanstack/solid-query': ^4.3.9 solid-js: ^1.5.1 - typescript: ^4.8.2 + typescript: 4.7.4 vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: @@ -638,7 +638,7 @@ importers: solid-js: 1.5.4 devDependencies: '@tanstack/eslint-plugin-query': link:../../../packages/eslint-plugin-query - typescript: 4.8.3 + typescript: 4.7.4 vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 @@ -646,7 +646,7 @@ importers: specifiers: '@tanstack/vue-query': ^4.13.3 '@vitejs/plugin-vue': ^3.1.2 - typescript: ^4.8.4 + typescript: 4.7.4 vite: ^3.1.8 vue: ^3.2.41 dependencies: @@ -654,7 +654,7 @@ importers: vue: 3.2.41 devDependencies: '@vitejs/plugin-vue': 3.1.2_vite@3.1.8+vue@3.2.41 - typescript: 4.8.4 + typescript: 4.7.4 vite: 3.1.8 examples/vue/dependent-queries: @@ -5960,7 +5960,7 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin/5.41.0_b2esjfxatoxyearpbj3ml2e4hy: + /@typescript-eslint/eslint-plugin/5.41.0_c2flhriocdzler6lrwbyxxyoca: resolution: {integrity: sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -5971,22 +5971,21 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/parser': 5.41.0_eslint@8.26.0 '@typescript-eslint/scope-manager': 5.41.0 - '@typescript-eslint/type-utils': 5.41.0_dyxdave6dwjbccc5dgiifcmuza - '@typescript-eslint/utils': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/type-utils': 5.41.0_eslint@8.26.0 + '@typescript-eslint/utils': 5.41.0_eslint@8.26.0 debug: 4.3.4 - eslint: 7.32.0 + eslint: 8.26.0 ignore: 5.2.0 regexpp: 3.2.0 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.3 - typescript: 4.8.3 + tsutils: 3.21.0 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/eslint-plugin/5.41.0_c2flhriocdzler6lrwbyxxyoca: + /@typescript-eslint/eslint-plugin/5.41.0_i2aae4mvohkbme6pfmscna5hqe: resolution: {integrity: sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -5997,16 +5996,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.41.0_eslint@8.26.0 + '@typescript-eslint/parser': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu '@typescript-eslint/scope-manager': 5.41.0 - '@typescript-eslint/type-utils': 5.41.0_eslint@8.26.0 - '@typescript-eslint/utils': 5.41.0_eslint@8.26.0 + '@typescript-eslint/type-utils': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu + '@typescript-eslint/utils': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu debug: 4.3.4 - eslint: 8.26.0 + eslint: 7.32.0 ignore: 5.2.0 regexpp: 3.2.0 semver: 7.3.7 - tsutils: 3.21.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -6065,7 +6065,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.41.0_dyxdave6dwjbccc5dgiifcmuza: + /@typescript-eslint/parser/5.41.0_eslint@8.26.0: resolution: {integrity: sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6077,15 +6077,14 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.41.0 '@typescript-eslint/types': 5.41.0 - '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.8.3 + '@typescript-eslint/typescript-estree': 5.41.0 debug: 4.3.4 - eslint: 7.32.0 - typescript: 4.8.3 + eslint: 8.26.0 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.41.0_eslint@8.26.0: + /@typescript-eslint/parser/5.41.0_hxadhbs2xogijvk7vq4t2azzbu: resolution: {integrity: sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6097,9 +6096,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.41.0 '@typescript-eslint/types': 5.41.0 - '@typescript-eslint/typescript-estree': 5.41.0 + '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.7.4 debug: 4.3.4 - eslint: 8.26.0 + eslint: 7.32.0 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -6120,7 +6120,7 @@ packages: '@typescript-eslint/visitor-keys': 5.41.0 dev: true - /@typescript-eslint/type-utils/5.41.0_dyxdave6dwjbccc5dgiifcmuza: + /@typescript-eslint/type-utils/5.41.0_eslint@8.26.0: resolution: {integrity: sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6130,17 +6130,16 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.8.3 - '@typescript-eslint/utils': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/typescript-estree': 5.41.0 + '@typescript-eslint/utils': 5.41.0_eslint@8.26.0 debug: 4.3.4 - eslint: 7.32.0 - tsutils: 3.21.0_typescript@4.8.3 - typescript: 4.8.3 + eslint: 8.26.0 + tsutils: 3.21.0 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/type-utils/5.41.0_eslint@8.26.0: + /@typescript-eslint/type-utils/5.41.0_hxadhbs2xogijvk7vq4t2azzbu: resolution: {integrity: sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6150,11 +6149,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.41.0 - '@typescript-eslint/utils': 5.41.0_eslint@8.26.0 + '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.7.4 + '@typescript-eslint/utils': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu debug: 4.3.4 - eslint: 8.26.0 - tsutils: 3.21.0 + eslint: 7.32.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -6231,7 +6231,7 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.41.0_typescript@4.8.3: + /@typescript-eslint/typescript-estree/5.41.0_typescript@4.7.4: resolution: {integrity: sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6246,13 +6246,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.3 - typescript: 4.8.3 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.41.0_dyxdave6dwjbccc5dgiifcmuza: + /@typescript-eslint/utils/5.41.0_eslint@8.26.0: resolution: {integrity: sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6262,17 +6262,17 @@ packages: '@types/semver': 7.3.12 '@typescript-eslint/scope-manager': 5.41.0 '@typescript-eslint/types': 5.41.0 - '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.8.3 - eslint: 7.32.0 + '@typescript-eslint/typescript-estree': 5.41.0 + eslint: 8.26.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@7.32.0 + eslint-utils: 3.0.0_eslint@8.26.0 semver: 7.3.7 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils/5.41.0_eslint@8.26.0: + /@typescript-eslint/utils/5.41.0_hxadhbs2xogijvk7vq4t2azzbu: resolution: {integrity: sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6282,10 +6282,10 @@ packages: '@types/semver': 7.3.12 '@typescript-eslint/scope-manager': 5.41.0 '@typescript-eslint/types': 5.41.0 - '@typescript-eslint/typescript-estree': 5.41.0 - eslint: 8.26.0 + '@typescript-eslint/typescript-estree': 5.41.0_typescript@4.7.4 + eslint: 7.32.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.26.0 + eslint-utils: 3.0.0_eslint@7.32.0 semver: 7.3.7 transitivePeerDependencies: - supports-color @@ -8816,7 +8816,7 @@ packages: eslint: 7.32.0 dev: true - /eslint-config-react-app/5.2.1_g3icqb772h4a27ny4eeob5pf34: + /eslint-config-react-app/5.2.1_kl5dmxelusuapydtgrn7w3jwbq: resolution: {integrity: sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ==} peerDependencies: '@typescript-eslint/eslint-plugin': 2.x @@ -8833,8 +8833,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.41.0_b2esjfxatoxyearpbj3ml2e4hy - '@typescript-eslint/parser': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/eslint-plugin': 5.41.0_i2aae4mvohkbme6pfmscna5hqe + '@typescript-eslint/parser': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu babel-eslint: 10.1.0_eslint@7.32.0 confusing-browser-globals: 1.0.11 eslint: 7.32.0 @@ -8843,7 +8843,7 @@ packages: eslint-plugin-jsx-a11y: 6.6.0_eslint@7.32.0 eslint-plugin-react: 7.20.0_eslint@7.32.0 eslint-plugin-react-hooks: 4.6.0_eslint@7.32.0 - typescript: 4.8.3 + typescript: 4.7.4 dev: true /eslint-config-standard-jsx/8.1.0_c3l3lp52boouk5zx5rvypilpme: @@ -8961,7 +8961,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/parser': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu debug: 3.2.7 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 2.7.1_hpmu7kn6tcn2vnxpfzvv33bxmy @@ -9043,7 +9043,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.41.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/parser': 5.41.0_hxadhbs2xogijvk7vq4t2azzbu array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 @@ -10980,7 +10980,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.8.2_rb7lfb2dlgdf5f7m6mcvvespxa + ts-node: 10.8.2_x2utdhayajzrh747hktprshhby transitivePeerDependencies: - bufferutil - canvas @@ -15195,7 +15195,7 @@ packages: /ts-interface-checker/0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /ts-jest/27.1.5_kv3z5ukkp6hjlassekz7vtidta: + /ts-jest/27.1.5_f5rbw76fdbj6kcejdlin5zoydm: resolution: {integrity: sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -15227,11 +15227,11 @@ packages: lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.3.7 - typescript: 4.8.3 + typescript: 4.7.4 yargs-parser: 20.2.4 dev: true - /ts-node/10.8.2_rb7lfb2dlgdf5f7m6mcvvespxa: + /ts-node/10.8.2_x2utdhayajzrh747hktprshhby: resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} hasBin: true peerDependencies: @@ -15257,7 +15257,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.8.3 + typescript: 4.7.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -15336,14 +15336,14 @@ packages: typescript: 4.4.4 dev: true - /tsutils/3.21.0_typescript@4.8.3: + /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.8.3 + typescript: 4.7.4 dev: true /type-check/0.3.2: @@ -15416,8 +15416,8 @@ packages: hasBin: true dev: true - /typescript/4.8.3: - resolution: {integrity: sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==} + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true From af2e27ef377fd8c0e99cc9251f18017267509814 Mon Sep 17 00:00:00 2001 From: moshyfawn Date: Fri, 30 Dec 2022 16:30:40 -0500 Subject: [PATCH 004/314] refactor: rename useErrorBoundar option to throwError (#4697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor(query-core): rename useErrorBoundary query option to throwError * ♻️ refactor(react-query): rename useErrorBoundary query option to throwError * ♻️ refactor(react-query): rename useErrorBoundary mutation option to throwError * ♻️ refactor(solid-query): rename useErrorBoundary mutation option to throwError * ♻️ refactor(solid-query): rename useErrorBoundary query option to throwError * ✅ test(react-query): rename useErrorBoundary query option to throwError * ✅ test(react-query): rename useErrorBoundary mutation option to throwError * ✅ test(react-query-devtools): rename useErrorBoundary query option to throwError * ✅ test(solid-query): rename useErrorBoundary query option to throwError * ✅ test(solid-query): rename useErrorBoundary mutation option to throwError * 📝 docs(react-query): rename useErrorBoundary query option to throwError * 📝 docs(react-query): rename useErrorBoundary mutation option to throwError * ♻️ refactor(solid-query): rename useErrorBoundary util option to throwError * ♻️ refactor(react-query): rename useErrorBoundary util option to throwError * 📝 docs(react-query): add throwError to migration guide * 🎨 style: format ThrowError type * ♻️ refactor(query-core): rename throwError query option to throwErrors * ♻️ refactor(react-query): rename throwError query option to throwErrors * ♻️ refactor(react-query): rename throwError mutation option to throwErrors * ✅ test(react-query): rename throwError mutation option to throwErrors * ✅ test(react-query): rename throwError query option to throwErrors * ✅ test(react-query-devtools): rename throwError query option to throwErrors * 📝 docs(react-query): rename throwError mutation option to throwErrors * ✅ test(react-query-devtools): rename throwError query option to throwErrors * ✅ test(solid-query): rename throwError mutation option to throwErrors * ✅ test(solid-query): rename throwError query option to throwErrors * 📝 docs(react-query): rename throwError to throwErrors in migration guide * 📝 docs(react-query): rename throwError to throwErrors in reference * ✅ test(react-query): update suspense test desc Co-authored-by: Dominik Dorfmeister --- .../react/guides/migrating-to-react-query-5.md | 4 ++++ docs/react/guides/suspense.md | 4 ++-- docs/react/reference/QueryClient.md | 2 +- .../react/reference/QueryErrorResetBoundary.md | 2 +- docs/react/reference/useMutation.md | 6 +++--- docs/react/reference/useQuery.md | 6 +++--- packages/query-core/src/queryClient.ts | 4 ++-- packages/query-core/src/queryObserver.ts | 2 +- packages/query-core/src/types.ts | 15 +++++---------- .../src/__tests__/devtools.test.tsx | 4 ++-- .../__tests__/QueryResetErrorBoundary.test.tsx | 18 +++++++++--------- .../src/__tests__/suspense.test.tsx | 12 ++++++------ .../src/__tests__/useIsFetching.test.tsx | 2 +- .../src/__tests__/useIsMutating.test.tsx | 2 +- .../src/__tests__/useMutation.test.tsx | 8 ++++---- .../src/__tests__/useQueries.test.tsx | 18 +++++++++--------- .../src/__tests__/useQuery.test.tsx | 12 ++++++------ packages/react-query/src/errorBoundaryUtils.ts | 15 +++++---------- packages/react-query/src/useBaseQuery.ts | 2 +- packages/react-query/src/useMutation.ts | 2 +- packages/react-query/src/useQueries.ts | 2 +- packages/react-query/src/utils.ts | 10 +++++----- .../src/__tests__/createMutation.test.tsx | 8 ++++---- .../src/__tests__/createQuery.test.tsx | 12 ++++++------ .../src/__tests__/suspense.test.tsx | 12 ++++++------ .../src/__tests__/useIsFetching.test.tsx | 2 +- .../src/__tests__/useIsMutating.test.tsx | 2 +- packages/solid-query/src/createBaseQuery.ts | 2 +- packages/solid-query/src/createMutation.ts | 2 +- packages/solid-query/src/utils.ts | 10 +++++----- 30 files changed, 98 insertions(+), 104 deletions(-) diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index dee7d8b90a..dadae4b0a2 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -26,3 +26,7 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que ### The minimum required TypeScript version is now 4.7 Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. + +### The `useErrorBoundary` prop has been renamed to `throwErrors` + +To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. \ No newline at end of file diff --git a/docs/react/guides/suspense.md b/docs/react/guides/suspense.md index 2e2dab347c..63f692e30a 100644 --- a/docs/react/guides/suspense.md +++ b/docs/react/guides/suspense.md @@ -41,11 +41,11 @@ useQuery({ queryKey, queryFn, suspense: true }) When using suspense mode, `status` states and `error` objects are not needed and are then replaced by usage of the `React.Suspense` component (including the use of the `fallback` prop and React error boundaries for catching errors). Please read the [Resetting Error Boundaries](#resetting-error-boundaries) and look at the [Suspense Example](https://codesandbox.io/s/github/tannerlinsley/react-query/tree/main/examples/react/suspense) for more information on how to set up suspense mode. -In addition to queries behaving differently in suspense mode, mutations also behave a bit differently. By default, instead of supplying the `error` variable when a mutation fails, it will be thrown during the next render of the component it's used in and propagate to the nearest error boundary, similar to query errors. If you wish to disable this, you can set the `useErrorBoundary` option to `false`. If you wish that errors are not thrown at all, you can set the `throwOnError` option to `false` as well! +In addition to queries behaving differently in suspense mode, mutations also behave a bit differently. By default, instead of supplying the `error` variable when a mutation fails, it will be thrown during the next render of the component it's used in and propagate to the nearest error boundary, similar to query errors. If you wish to disable this, you can set the `throwErrors` option to `false`. If you wish that errors are not thrown at all, you can set the `throwOnError` option to `false` as well! ## Resetting Error Boundaries -Whether you are using **suspense** or **useErrorBoundaries** in your queries, you will need a way to let queries know that you want to try again when re-rendering after some error occurred. +Whether you are using **suspense** or **throwErrors** in your queries, you will need a way to let queries know that you want to try again when re-rendering after some error occurred. Query errors can be reset with the `QueryErrorResetBoundary` component or with the `useQueryErrorResetBoundary` hook. diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index fb7a6c8ec4..a253ab5e21 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -95,7 +95,7 @@ try { **Options** -The options for `fetchQuery` are exactly the same as those of [`useQuery`](../reference/useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, useErrorBoundary, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. +The options for `fetchQuery` are exactly the same as those of [`useQuery`](../reference/useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, throwErrors, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. **Returns** diff --git a/docs/react/reference/QueryErrorResetBoundary.md b/docs/react/reference/QueryErrorResetBoundary.md index 8263cb31c8..23acd76157 100644 --- a/docs/react/reference/QueryErrorResetBoundary.md +++ b/docs/react/reference/QueryErrorResetBoundary.md @@ -3,7 +3,7 @@ id: QueryErrorResetBoundary title: QueryErrorResetBoundary --- -When using **suspense** or **useErrorBoundaries** in your queries, you need a way to let queries know that you want to try again when re-rendering after some error occurred. With the `QueryErrorResetBoundary` component you can reset any query errors within the boundaries of the component. +When using **suspense** or **throwErrors** in your queries, you need a way to let queries know that you want to try again when re-rendering after some error occurred. With the `QueryErrorResetBoundary` component you can reset any query errors within the boundaries of the component. ```tsx import { QueryErrorResetBoundary } from '@tanstack/react-query' diff --git a/docs/react/reference/useMutation.md b/docs/react/reference/useMutation.md index c3ae969ce6..eba51cdafb 100644 --- a/docs/react/reference/useMutation.md +++ b/docs/react/reference/useMutation.md @@ -29,7 +29,7 @@ const { onSuccess, retry, retryDelay, - useErrorBoundary, + throwErrors, meta }) @@ -82,8 +82,8 @@ mutate(variables, { - This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the next attempt in milliseconds. - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. - A function like `attempt => attempt * 1000` applies linear backoff. -- `useErrorBoundary: undefined | boolean | (error: TError) => boolean` - - Defaults to the global query config's `useErrorBoundary` value, which is `undefined` +- `throwErrors: undefined | boolean | (error: TError) => boolean` + - Defaults to the global query config's `throwErrors` value, which is `undefined` - Set this to `true` if you want mutation errors to be thrown in the render phase and propagate to the nearest error boundary - Set this to `false` to disable the behavior of throwing errors to the error boundary. - If set to a function, it will be passed the error and should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`) diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index 2531bf39a6..eecd1e23a1 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -55,7 +55,7 @@ const { staleTime, structuralSharing, suspense, - useErrorBoundary, + throwErrors, }) ``` @@ -178,8 +178,8 @@ const { - Defaults to `true` - If set to `false`, structural sharing between query results will be disabled. - If set to a function, the old and new data values will be passed through this function, which should combine them into resolved data for the query. This way, you can retain references from the old data to improve performance even when that data contains non-serializable values. -- `useErrorBoundary: undefined | boolean | (error: TError, query: Query) => boolean` - - Defaults to the global query config's `useErrorBoundary` value, which is `undefined` +- `throwErrors: undefined | boolean | (error: TError, query: Query) => boolean` + - Defaults to the global query config's `throwErrors` value, which is `undefined` - Set this to `true` if you want errors to be thrown in the render phase and propagate to the nearest error boundary - Set this to `false` to disable `suspense`'s default behavior of throwing errors to the error boundary. - If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`) diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 62aebf93a6..4f0ce0540c 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -749,8 +749,8 @@ export class QueryClient { defaultedOptions.refetchOnReconnect = defaultedOptions.networkMode !== 'always' } - if (typeof defaultedOptions.useErrorBoundary === 'undefined') { - defaultedOptions.useErrorBoundary = !!defaultedOptions.suspense + if (typeof defaultedOptions.throwErrors === 'undefined') { + defaultedOptions.throwErrors = !!defaultedOptions.suspense } return defaultedOptions as DefaultedQueryObserverOptions< diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 9e4b85634c..22832d023f 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -618,7 +618,7 @@ export class QueryObserver< const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) - if (this.options.useErrorBoundary) { + if (this.options.throwErrors) { includedProps.add('error') } diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 472593801b..2ce6df9f64 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -104,7 +104,7 @@ export interface QueryOptions< meta?: QueryMeta } -export type UseErrorBoundary< +export type ThrowErrors< TQueryFnData, TError, TQueryData, @@ -221,12 +221,7 @@ export interface QueryObserverOptions< * If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`). * Defaults to `false`. */ - useErrorBoundary?: UseErrorBoundary< - TQueryFnData, - TError, - TQueryData, - TQueryKey - > + throwErrors?: ThrowErrors /** * This option can be used to transform or select a part of the data returned by the query function. */ @@ -260,7 +255,7 @@ export type DefaultedQueryObserverOptions< TQueryKey extends QueryKey = QueryKey, > = WithRequired< QueryObserverOptions, - 'useErrorBoundary' | 'refetchOnReconnect' + 'throwErrors' | 'refetchOnReconnect' > export interface InfiniteQueryObserverOptions< @@ -291,7 +286,7 @@ export type DefaultedInfiniteQueryObserverOptions< TQueryData, TQueryKey >, - 'useErrorBoundary' | 'refetchOnReconnect' + 'throwErrors' | 'refetchOnReconnect' > export interface FetchQueryOptions< @@ -587,7 +582,7 @@ export interface MutationObserverOptions< TVariables = void, TContext = unknown, > extends MutationOptions { - useErrorBoundary?: boolean | ((error: TError) => boolean) + throwErrors?: boolean | ((error: TError) => boolean) } export interface MutateOptions< diff --git a/packages/react-query-devtools/src/__tests__/devtools.test.tsx b/packages/react-query-devtools/src/__tests__/devtools.test.tsx index be0150f3b3..61d00c6b45 100644 --- a/packages/react-query-devtools/src/__tests__/devtools.test.tsx +++ b/packages/react-query-devtools/src/__tests__/devtools.test.tsx @@ -704,7 +704,7 @@ describe('ReactQueryDevtools', () => { function Page() { const { data = 'default' } = useQuery(['check'], async () => 'test', { - useErrorBoundary: true, + throwErrors: true, }) return ( @@ -739,7 +739,7 @@ describe('ReactQueryDevtools', () => { function Page() { const { data = 'default' } = useQuery(['check'], async () => 'test', { - useErrorBoundary: true, + throwErrors: true, context, }) diff --git a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx index 3c9ff9c2e2..49fc6d5fad 100644 --- a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx +++ b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx @@ -34,7 +34,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, }, ) return
{data}
@@ -91,7 +91,7 @@ describe('QueryErrorResetBoundary', () => { { retry: false, enabled: !succeed, - useErrorBoundary: true, + throwErrors: true, }, ) return ( @@ -154,7 +154,7 @@ describe('QueryErrorResetBoundary', () => { { retry: false, enabled, - useErrorBoundary: true, + throwErrors: true, }, ) @@ -209,7 +209,7 @@ describe('QueryErrorResetBoundary', () => { { retry: false, enabled: false, - useErrorBoundary: true, + throwErrors: true, }, ) @@ -274,7 +274,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, }, ) return
{data}
@@ -329,7 +329,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, initialData: 'initial', }, ) @@ -387,7 +387,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, }, ) return
{data}
@@ -447,7 +447,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, }, ) return
{data}
@@ -589,7 +589,7 @@ describe('QueryErrorResetBoundary', () => { }, { retry: false, - useErrorBoundary: true, + throwErrors: true, }, ) return
{data}
diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 114e0f9059..af29d07a27 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -615,7 +615,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('error boundary')) }) - it('should not throw errors to the error boundary when useErrorBoundary: false', async () => { + it('should not throw errors to the error boundary when throwErrors: false', async () => { const key = queryKey() function Page() { @@ -628,7 +628,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: false, + throwErrors: false, }, ) return
rendered
@@ -656,7 +656,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('rendered')) }) - it('should not throw errors to the error boundary when a useErrorBoundary function returns true', async () => { + it('should not throw errors to the error boundary when a throwErrors function returns true', async () => { const key = queryKey() function Page() { @@ -669,7 +669,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) return
rendered
@@ -697,7 +697,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('error boundary')) }) - it('should not throw errors to the error boundary when a useErrorBoundary function returns false', async () => { + it('should not throw errors to the error boundary when a throwErrors function returns false', async () => { const key = queryKey() function Page() { @@ -710,7 +710,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) return
rendered
diff --git a/packages/react-query/src/__tests__/useIsFetching.test.tsx b/packages/react-query/src/__tests__/useIsFetching.test.tsx index 05e953836d..d94cd91064 100644 --- a/packages/react-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/react-query/src/__tests__/useIsFetching.test.tsx @@ -224,7 +224,7 @@ describe('useIsFetching', () => { useQuery(key, async () => 'test', { enabled: true, context, - useErrorBoundary: true, + throwErrors: true, }) return ( diff --git a/packages/react-query/src/__tests__/useIsMutating.test.tsx b/packages/react-query/src/__tests__/useIsMutating.test.tsx index 6e65de2612..67940e7b85 100644 --- a/packages/react-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/react-query/src/__tests__/useIsMutating.test.tsx @@ -234,7 +234,7 @@ describe('useIsMutating', () => { function Page() { const { mutate } = useMutation(['mutation'], async () => 'data', { - useErrorBoundary: true, + throwErrors: true, context, }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index fe626500ad..78b77fc740 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -696,7 +696,7 @@ describe('useMutation', () => { fireEvent.click(getByText('unmount')) }) - it('should be able to throw an error when useErrorBoundary is set to true', async () => { + it('should be able to throw an error when throwErrors is set to true', async () => { function Page() { const { mutate } = useMutation( () => { @@ -704,7 +704,7 @@ describe('useMutation', () => { err.stack = '' return Promise.reject(err) }, - { useErrorBoundary: true }, + { throwErrors: true }, ) return ( @@ -734,7 +734,7 @@ describe('useMutation', () => { }) }) - it('should be able to throw an error when useErrorBoundary is a function that returns true', async () => { + it('should be able to throw an error when throwErrors is a function that returns true', async () => { let boundary = false function Page() { const { mutate, error } = useMutation( @@ -744,7 +744,7 @@ describe('useMutation', () => { return Promise.reject(err) }, { - useErrorBoundary: () => { + throwErrors: () => { boundary = !boundary return !boundary }, diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 83a7e617fa..9434d33a87 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -1103,7 +1103,7 @@ describe('useQueries', () => { }) }) - it("should throw error if in one of queries' queryFn throws and useErrorBoundary is in use", async () => { + it("should throw error if in one of queries' queryFn throws and throwErrors is in use", async () => { const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() @@ -1117,14 +1117,14 @@ describe('useQueries', () => { queryFn: () => Promise.reject( new Error( - 'this should not throw because useErrorBoundary is not set', + 'this should not throw because throwErrors is not set', ), ), }, { queryKey: key2, queryFn: () => Promise.reject(new Error('single query error')), - useErrorBoundary: true, + throwErrors: true, retry: false, }, { @@ -1137,7 +1137,7 @@ describe('useQueries', () => { Promise.reject( new Error('this should not throw because query#2 already did'), ), - useErrorBoundary: true, + throwErrors: true, retry: false, }, ], @@ -1164,7 +1164,7 @@ describe('useQueries', () => { await waitFor(() => rendered.getByText('single query error')) }) - it("should throw error if in one of queries' queryFn throws and useErrorBoundary function resolves to true", async () => { + it("should throw error if in one of queries' queryFn throws and throwErrors function resolves to true", async () => { const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() @@ -1178,10 +1178,10 @@ describe('useQueries', () => { queryFn: () => Promise.reject( new Error( - 'this should not throw because useErrorBoundary function resolves to false', + 'this should not throw because throwErrors function resolves to false', ), ), - useErrorBoundary: () => false, + throwErrors: () => false, retry: false, }, { @@ -1191,7 +1191,7 @@ describe('useQueries', () => { { queryKey: key3, queryFn: () => Promise.reject(new Error('single query error')), - useErrorBoundary: () => true, + throwErrors: () => true, retry: false, }, { @@ -1200,7 +1200,7 @@ describe('useQueries', () => { Promise.reject( new Error('this should not throw because query#3 already did'), ), - useErrorBoundary: true, + throwErrors: true, retry: false, }, ], diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index a2598f7577..ec60ff9baa 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -2806,14 +2806,14 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('Error test jaylen')) }) - it('should throw error if queryFn throws and useErrorBoundary is in use', async () => { + it('should throw error if queryFn throws and throwErrors is in use', async () => { const key = queryKey() function Page() { const { status, error } = useQuery( key, () => Promise.reject('Error test jaylen'), - { retry: false, useErrorBoundary: true }, + { retry: false, throwErrors: true }, ) return ( @@ -2834,14 +2834,14 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error boundary')) }) - it('should update with data if we observe no properties and useErrorBoundary', async () => { + it('should update with data if we observe no properties and throwErrors', async () => { const key = queryKey() let result: UseQueryResult | undefined function Page() { const query = useQuery(key, () => Promise.resolve('data'), { - useErrorBoundary: true, + throwErrors: true, }) React.useEffect(() => { @@ -2869,7 +2869,7 @@ describe('useQuery', () => { () => Promise.reject('Local Error'), { retry: false, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) @@ -2901,7 +2901,7 @@ describe('useQuery', () => { () => Promise.reject(new Error('Remote Error')), { retry: false, - useErrorBoundary: (err) => err.message !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', }, ) diff --git a/packages/react-query/src/errorBoundaryUtils.ts b/packages/react-query/src/errorBoundaryUtils.ts index 87e4ee1afd..c13d2792e5 100644 --- a/packages/react-query/src/errorBoundaryUtils.ts +++ b/packages/react-query/src/errorBoundaryUtils.ts @@ -3,7 +3,7 @@ import type { Query, QueryKey, QueryObserverResult, - UseErrorBoundary, + ThrowErrors, } from '@tanstack/query-core' import type { QueryErrorResetBoundaryValue } from './QueryErrorResetBoundary' import * as React from 'react' @@ -25,7 +25,7 @@ export const ensurePreventErrorBoundaryRetry = < >, errorResetBoundary: QueryErrorResetBoundaryValue, ) => { - if (options.suspense || options.useErrorBoundary) { + if (options.suspense || options.throwErrors) { // Prevent retrying failed query if the error boundary has not been reset yet if (!errorResetBoundary.isReset()) { options.retryOnMount = false @@ -50,23 +50,18 @@ export const getHasError = < >({ result, errorResetBoundary, - useErrorBoundary, + throwErrors, query, }: { result: QueryObserverResult errorResetBoundary: QueryErrorResetBoundaryValue - useErrorBoundary: UseErrorBoundary< - TQueryFnData, - TError, - TQueryData, - TQueryKey - > + throwErrors: ThrowErrors query: Query }) => { return ( result.isError && !errorResetBoundary.isReset() && !result.isFetching && - shouldThrowError(useErrorBoundary, [result.error, query]) + shouldThrowError(throwErrors, [result.error, query]) ) } diff --git a/packages/react-query/src/useBaseQuery.ts b/packages/react-query/src/useBaseQuery.ts index df537ab745..a3b201e1cd 100644 --- a/packages/react-query/src/useBaseQuery.ts +++ b/packages/react-query/src/useBaseQuery.ts @@ -102,7 +102,7 @@ export function useBaseQuery< getHasError({ result, errorResetBoundary, - useErrorBoundary: defaultedOptions.useErrorBoundary, + throwErrors: defaultedOptions.throwErrors, query: observer.getCurrentQuery(), }) ) { diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index 8ba94a3bfb..a727fac112 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -113,7 +113,7 @@ export function useMutation< if ( result.error && - shouldThrowError(observer.options.useErrorBoundary, [result.error]) + shouldThrowError(observer.options.throwErrors, [result.error]) ) { throw result.error } diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index 0682d30f1e..9251ee7cbd 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -234,7 +234,7 @@ export function useQueries({ getHasError({ result, errorResetBoundary, - useErrorBoundary: defaultedQueries[index]?.useErrorBoundary ?? false, + throwErrors: defaultedQueries[index]?.throwErrors ?? false, query: observer.getQueries()[index]!, }), ) diff --git a/packages/react-query/src/utils.ts b/packages/react-query/src/utils.ts index c0d2089045..212f4a4817 100644 --- a/packages/react-query/src/utils.ts +++ b/packages/react-query/src/utils.ts @@ -1,11 +1,11 @@ export function shouldThrowError boolean>( - _useErrorBoundary: boolean | T | undefined, + throwError: boolean | T | undefined, params: Parameters, ): boolean { - // Allow useErrorBoundary function to override throwing behavior on a per-error basis - if (typeof _useErrorBoundary === 'function') { - return _useErrorBoundary(...params) + // Allow throwError function to override throwing behavior on a per-error basis + if (typeof throwError === 'function') { + return throwError(...params) } - return !!_useErrorBoundary + return !!throwError } diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index ffe7c1ca09..68213dfb86 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -782,7 +782,7 @@ describe('useMutation', () => { fireEvent.click(screen.getByText('unmount')) }) - it('should be able to throw an error when useErrorBoundary is set to true', async () => { + it('should be able to throw an error when throwErrors is set to true', async () => { function Page() { const mutation = createMutation( () => { @@ -790,7 +790,7 @@ describe('useMutation', () => { err.stack = '' return Promise.reject(err) }, - { useErrorBoundary: true }, + { throwErrors: true }, ) return ( @@ -821,7 +821,7 @@ describe('useMutation', () => { }) }) - it('should be able to throw an error when useErrorBoundary is a function that returns true', async () => { + it('should be able to throw an error when throwErrors is a function that returns true', async () => { let boundary = false function Page() { const mutation = createMutation( @@ -831,7 +831,7 @@ describe('useMutation', () => { return Promise.reject(err) }, { - useErrorBoundary: () => { + throwErrors: () => { boundary = !boundary return !boundary }, diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 47e0ada36e..7549e1e817 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -2896,14 +2896,14 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('Error test jaylen')) }) - it('should throw error if queryFn throws and useErrorBoundary is in use', async () => { + it('should throw error if queryFn throws and throwErrors is in use', async () => { const key = queryKey() function Page() { const state = createQuery( key, () => Promise.reject('Error test jaylen'), - { retry: false, useErrorBoundary: true }, + { retry: false, throwErrors: true }, ) return ( @@ -2925,14 +2925,14 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('error boundary')) }) - it('should update with data if we observe no properties and useErrorBoundary', async () => { + it('should update with data if we observe no properties and throwErrors', async () => { const key = queryKey() let result: CreateQueryResult | undefined function Page() { const query = createQuery(key, () => Promise.resolve('data'), { - useErrorBoundary: true, + throwErrors: true, }) createEffect(() => { @@ -2962,7 +2962,7 @@ describe('createQuery', () => { () => Promise.reject('Local Error'), { retry: false, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) @@ -2995,7 +2995,7 @@ describe('createQuery', () => { () => Promise.reject(new Error('Remote Error')), { retry: false, - useErrorBoundary: (err) => err.message !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', }, ) diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index 543de40e5d..5da5cf1501 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -604,7 +604,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('error boundary')) }) - it('should not throw errors to the error boundary when useErrorBoundary: false', async () => { + it('should not throw errors to the error boundary when throwErrors: false', async () => { const key = queryKey() function Page() { @@ -617,7 +617,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: false, + throwErrors: false, }, ) @@ -656,7 +656,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('rendered')) }) - it('should not throw errors to the error boundary when a useErrorBoundary function returns true', async () => { + it('should not throw errors to the error boundary when a throwErrors function returns true', async () => { const key = queryKey() function Page() { @@ -669,7 +669,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) @@ -708,7 +708,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('error boundary')) }) - it('should not throw errors to the error boundary when a useErrorBoundary function returns false', async () => { + it('should not throw errors to the error boundary when a throwErrors function returns false', async () => { const key = queryKey() function Page() { @@ -721,7 +721,7 @@ describe("useQuery's in Suspense mode", () => { { retry: false, suspense: true, - useErrorBoundary: (err) => err !== 'Local Error', + throwErrors: (err) => err !== 'Local Error', }, ) diff --git a/packages/solid-query/src/__tests__/useIsFetching.test.tsx b/packages/solid-query/src/__tests__/useIsFetching.test.tsx index 2099a385ef..ad1a48fb66 100644 --- a/packages/solid-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/solid-query/src/__tests__/useIsFetching.test.tsx @@ -245,7 +245,7 @@ describe('useIsFetching', () => { createQuery(key, async () => 'test', { enabled: true, context, - useErrorBoundary: true, + throwErrors: true, }) return ( diff --git a/packages/solid-query/src/__tests__/useIsMutating.test.tsx b/packages/solid-query/src/__tests__/useIsMutating.test.tsx index 64e1f9f6dd..faa285ee22 100644 --- a/packages/solid-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/solid-query/src/__tests__/useIsMutating.test.tsx @@ -276,7 +276,7 @@ describe('useIsMutating', () => { function Page() { const { mutate } = createMutation(['mutation'], async () => 'data', { - useErrorBoundary: true, + throwErrors: true, context, }) diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts index de4c96d6fd..7df614a9f0 100644 --- a/packages/solid-query/src/createBaseQuery.ts +++ b/packages/solid-query/src/createBaseQuery.ts @@ -105,7 +105,7 @@ export function createBaseQuery< if ( state.isError && !state.isFetching && - shouldThrowError(observer.options.useErrorBoundary, [ + shouldThrowError(observer.options.throwErrors, [ state.error, observer.getCurrentQuery(), ]) diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index 2ecb4ad34d..0ebc56ec77 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -106,7 +106,7 @@ export function createMutation< () => { if ( state.isError && - shouldThrowError(observer.options.useErrorBoundary, [state.error]) + shouldThrowError(observer.options.throwErrors, [state.error]) ) { throw state.error } diff --git a/packages/solid-query/src/utils.ts b/packages/solid-query/src/utils.ts index 6e418e5911..22ee398604 100644 --- a/packages/solid-query/src/utils.ts +++ b/packages/solid-query/src/utils.ts @@ -59,15 +59,15 @@ export function parseFilterArgs< } export function shouldThrowError boolean>( - _useErrorBoundary: boolean | T | undefined, + throwError: boolean | T | undefined, params: Parameters, ): boolean { - // Allow useErrorBoundary function to override throwing behavior on a per-error basis - if (typeof _useErrorBoundary === 'function') { - return _useErrorBoundary(...params) + // Allow throwError function to override throwing behavior on a per-error basis + if (typeof throwError === 'function') { + return throwError(...params) } - return !!_useErrorBoundary + return !!throwError } export function sleep(timeout: number): Promise { From eeec5f77bc9a703ffb6a6d283dcedada34aa3c75 Mon Sep 17 00:00:00 2001 From: "Mahmoud M. Anwar" Date: Sat, 31 Dec 2022 11:29:34 +0200 Subject: [PATCH 005/314] feat: remove overloads (#4714) * refactor(query-core): remove overloads remove overloads and only allow all functions to be called with a single signature BREAKING CHANGE: Overloads are removed, query-core now supports a single signature * test(query-core): apply single signature to all tests * refactor(react-query): remove overloads remove overloads and only allow all functions to be called with a single signature BREAKING CHANGE: Overloads are removed, react-query now supports a single signature * test(react-query): apply single signature to all tests * refactor(solid-query): remove overloads remove overloads and only allow all functions to be called with a single signature BREAKING CHANGE: Overloads are removed, solid-query now supports a single signature * test(solid-query): apply single signature to all tests * refactor(vue-query): remove overloads remove overloads and only allow all functions to be called with a single signature BREAKING CHANGE: Overloads are removed, vue-query now supports a single signature * test(vue-query): apply single signature to all tests * test(react-query-persist-client): apply single signature to all tests * fix(react-query-devtools): apply single signature to all tests * test(react-query-devtools): apply single signature to all tests * test(query-sync-storage-persister): apply single signature to all tests * docs: apply object single signature to all docs * docs(examples/react): apply object single signature to all examples * docs(examples/solid): apply object single signature to all examples * docs(examples/vue): apply object single signature to all examples * style(examples): run prettier on all files * docs: use multiline whenever we use the object syntax * fix(setQueryDefaults): keep it as two arguments * fix(createMutation): remove unnecessary shallow copy * fix(vue-query): rename parseArgs functions to unrefArgs * docs(migrating-to-react-query-5): list all affected functions * fix(setQueryData): remove as alias * refactor(getQueryData): getQueryData now supports only queryKey as an argument BREAKING CHANGE: getQueryData now accepts only queryKey as an argument * refactor(getQueryState): getQueryState now supports only queryKey as an argument BREAKING CHANGE: getQueryState now accepts only queryKey as an argument * test(react-query/useQuery): missing single signature --- docs/react/adapters/solid-query.md | 40 +- .../community/liaoliao666-react-query-kit.md | 5 +- .../guides/migrating-to-react-query-5.md | 90 ++ docs/react/reference/QueryClient.md | 6 +- docs/solid/overview.md | 40 +- .../load-more-infinite-scroll/pages/index.js | 14 +- .../src/screens/MovieDetailsScreen.tsx | 10 +- .../src/screens/MoviesListScreen.tsx | 8 +- .../react/react-router/src/routes/root.jsx | 2 +- examples/solid/basic-typescript/src/index.tsx | 12 +- examples/vue/dependent-queries/src/Post.vue | 17 +- examples/vue/dependent-queries/src/Posts.vue | 8 +- packages/query-core/src/index.ts | 11 +- packages/query-core/src/queryCache.ts | 15 +- packages/query-core/src/queryClient.ts | 322 +---- .../query-core/src/tests/hydration.test.tsx | 194 ++- .../src/tests/queriesObserver.test.tsx | 8 +- packages/query-core/src/tests/query.test.tsx | 234 ++-- .../query-core/src/tests/queryCache.test.tsx | 179 ++- .../query-core/src/tests/queryClient.test.tsx | 334 +++-- .../src/tests/queryObserver.test.tsx | 16 +- packages/query-core/src/tests/utils.test.tsx | 8 - packages/query-core/src/types.ts | 5 +- packages/query-core/src/utils.ts | 78 +- .../src/__tests__/storageIsFull.test.ts | 62 +- .../src/__tests__/devtools.test.tsx | 203 +-- .../react-query-devtools/src/devtools.tsx | 10 +- .../PersistQueryClientProvider.test.tsx | 91 +- .../src/__tests__/Hydrate.test.tsx | 40 +- .../__tests__/QueryClientProvider.test.tsx | 66 +- .../QueryResetErrorBoundary.test.tsx | 148 +-- .../src/__tests__/ssr-hydration.test.tsx | 26 +- .../react-query/src/__tests__/ssr.test.tsx | 21 +- .../src/__tests__/suspense.test.tsx | 288 ++--- .../src/__tests__/useInfiniteQuery.test.tsx | 314 +++-- .../src/__tests__/useIsFetching.test.tsx | 77 +- .../src/__tests__/useIsMutating.test.tsx | 89 +- .../src/__tests__/useMutation.test.tsx | 240 ++-- .../src/__tests__/useQuery.test.tsx | 1121 ++++++++++------- .../src/__tests__/useQuery.types.test.tsx | 35 +- packages/react-query/src/types.ts | 30 +- packages/react-query/src/useInfiniteQuery.ts | 81 +- packages/react-query/src/useIsFetching.ts | 14 +- packages/react-query/src/useIsMutating.ts | 18 +- packages/react-query/src/useMutation.ts | 61 +- packages/react-query/src/useQuery.ts | 114 +- .../__tests__/QueryClientProvider.test.tsx | 65 +- .../__tests__/createInfiniteQuery.test.tsx | 328 +++-- .../src/__tests__/createMutation.test.tsx | 244 ++-- .../src/__tests__/createQuery.test.tsx | 1095 +++++++++------- .../src/__tests__/createQuery.types.test.tsx | 33 +- .../src/__tests__/suspense.test.tsx | 285 ++--- .../src/__tests__/transition.test.tsx | 9 +- .../src/__tests__/useIsFetching.test.tsx | 83 +- .../src/__tests__/useIsMutating.test.tsx | 89 +- .../solid-query/src/createInfiniteQuery.ts | 83 +- packages/solid-query/src/createMutation.ts | 64 +- packages/solid-query/src/createQuery.ts | 127 +- packages/solid-query/src/types.ts | 17 +- packages/solid-query/src/useIsFetching.ts | 30 +- packages/solid-query/src/useIsMutating.ts | 17 +- packages/solid-query/src/utils.ts | 58 +- .../vue-query/src/__mocks__/useBaseQuery.ts | 4 +- .../src/__tests__/queryCache.test.ts | 22 +- .../src/__tests__/queryClient.test.ts | 237 +--- .../src/__tests__/useInfiniteQuery.test.ts | 8 +- .../__tests__/useInfiniteQuery.types.test.tsx | 5 + .../src/__tests__/useIsFetching.test.ts | 39 +- .../src/__tests__/useIsMutating.test.ts | 39 +- .../src/__tests__/useMutation.test.ts | 97 +- .../vue-query/src/__tests__/useQuery.test.ts | 103 +- .../src/__tests__/useQuery.types.test.tsx | 5 + .../src/__tests__/vueQueryPlugin.test.ts | 4 +- packages/vue-query/src/devtools/devtools.ts | 2 +- packages/vue-query/src/queryCache.ts | 37 +- packages/vue-query/src/queryClient.ts | 440 ++----- packages/vue-query/src/useBaseQuery.ts | 49 +- packages/vue-query/src/useInfiniteQuery.ts | 70 +- packages/vue-query/src/useIsFetching.ts | 37 +- packages/vue-query/src/useIsMutating.ts | 34 +- packages/vue-query/src/useMutation.ts | 95 +- packages/vue-query/src/useQuery.ts | 123 +- 82 files changed, 4032 insertions(+), 4850 deletions(-) diff --git a/docs/react/adapters/solid-query.md b/docs/react/adapters/solid-query.md index bf49538831..3a4dbf5e96 100644 --- a/docs/react/adapters/solid-query.md +++ b/docs/react/adapters/solid-query.md @@ -13,7 +13,10 @@ import { Switch, Match, For } from 'solid-js' const queryClient = new QueryClient() function Example() { - const query = createQuery(() => ['todos'], fetchTodos) + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos + }) return (
@@ -69,10 +72,16 @@ Solid Query offers an API similar to React Query, but there are some key differ ```tsx // ❌ react version -useQuery(["todos", todo], fetchTodos) +useQuery({ + queryKey: ["todos", todo], + queryFn: fetchTodos +}) // ✅ solid version -createQuery(() => ["todos", todo()], fetchTodos) +createQuery({ + queryKey: () => ["todos", todo()], + queryFn: fetchTodos +}) ``` - Suspense works for queries out of the box if you access the query data inside a `` boundary. @@ -81,7 +90,11 @@ createQuery(() => ["todos", todo()], fetchTodos) import { For, Suspense } from 'solid-js' function Example() { - const query = createQuery(() => ['todos'], fetchTodos) + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos + }) + return (
{/* ✅ Will trigger loading fallback, data accessed in a suspense context. */} @@ -113,20 +126,21 @@ export default function App() { function Example() { // ❌ react version -- supports destructing outside reactive context - // const { isLoading, error, data } = useQuery(['repoData'], () => - // fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => + // const { isLoading, error, data } = useQuery({ + // queryKey: ['repoData'], () => + // queryFn: fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => // res.json() // ) - // ) + // }) // ✅ solid version -- does not support destructuring outside reactive context - const query = createQuery( - () => ['repoData'], - () => + const query = createQuery({ + queryKey: () => ['repoData'], + queryFn: () => fetch('https://api.github.com/repos/tannerlinsley/react-query').then( (res) => res.json(), ), - ) + }) // ✅ access query properties in JSX reactive context return ( @@ -161,7 +175,9 @@ const queryClient = new QueryClient() function Example() { const [enabled, setEnabled] = createSignal(false) - const query = createQuery(() => ['todos'], fetchTodos, { + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos, // ❌ passing a signal directly is not reactive // enabled: enabled(), diff --git a/docs/react/community/liaoliao666-react-query-kit.md b/docs/react/community/liaoliao666-react-query-kit.md index 3e5ee6ab3e..08685601a7 100644 --- a/docs/react/community/liaoliao666-react-query-kit.md +++ b/docs/react/community/liaoliao666-react-query-kit.md @@ -66,7 +66,10 @@ console.log(usePost.getKey(variables)) // ['/posts', { id: 1 }] export async function getStaticProps() { const queryClient = new QueryClient() - await queryClient.prefetchQuery(usePost.getKey(variables), usePost.queryFn) + await queryClient.prefetchQuery({ + queryKey: usePost.getKey(variables), + queryFn: usePost.queryFn + }) return { props: { diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index dadae4b0a2..89e637710f 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -7,6 +7,96 @@ title: Migrating to TanStack Query v5 v5 is a major version, so there are some breaking changes to be aware of: +### Supports a single signature, one object + +useQuery and friends used to have many overloads in TypeScript - different ways how the function can be invoked. Not only this was tough to maintain, type wise, it also required a runtime check to see which type the first and the second parameter, to correctly create options. + +now we only support the object format. + +```diff +- useQuery(key, fn, options) ++ useQuery({ queryKey, queryFn, ...options }) + +- useInfiniteQuery(key, fn, options) ++ useInfiniteQuery({ queryKey, queryFn, ...options }) + +- useMutation(fn, options) ++ useMutation({ mutationFn, ...options }) + +- useIsFetching(key, filters) ++ useIsFetching({ queryKey, ...filters }) + +- useIsMutating(key, filters) ++ useIsMutating({ mutationKey, ...filters }) +``` + +```diff +- queryClient.isFetching(key, filters) ++ queryClient.isFetching({ queryKey, ...filters }) + +- queryClient.ensureQueryData(key, filters) ++ queryClient.ensureQueryData({ queryKey, ...filters }) + +- queryClient.getQueriesData(key, filters) ++ queryClient.getQueriesData({ queryKey, ...filters }) + +- queryClient.setQueriesData(key, updater, filters, options) ++ queryClient.setQueriesData({ queryKey, ...filters }, updater, options) + +- queryClient.removeQueries(key, filters) ++ queryClient.removeQueries({ queryKey, ...filters }) + +- queryClient.resetQueries(key, filters, options) ++ queryClient.resetQueries({ queryKey, ...filters }, options) + +- queryClient.cancelQueries(key, filters, options) ++ queryClient.cancelQueries({ queryKey, ...filters }, options) + +- queryClient.invalidateQueries(key, filters, options) ++ queryClient.invalidateQueries({ queryKey, ...filters }, options) + +- queryClient.refetchQueries(key, filters, options) ++ queryClient.refetchQueries({ queryKey, ...filters }, options) + +- queryClient.fetchQuery(key, fn, options) ++ queryClient.fetchQuery({ queryKey, queryFn, ...options }) + +- queryClient.prefetchQuery(key, fn, options) ++ queryClient.prefetchQuery({ queryKey, queryFn, ...options }) + +- queryClient.fetchInfiniteQuery(key, fn, options) ++ queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) + +- queryClient.prefetchInfiniteQuery(key, fn, options) ++ queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) +``` + +```diff +- queryCache.find(key, filters) ++ queryCache.find({ queryKey, ...filters }) + +- queryCache.findAll(key, filters) ++ queryCache.findAll({ queryKey, ...filters }) +``` + +### `queryClient.getQueryData` now accepts queryKey only as an Argument + +`queryClient.getQueryData` argument is changed to accept only a `queryKey` + +```diff +- queryClient.getQueryData(queryKey, filters) ++ queryClient.getQueryData(queryKey) +``` + +### `queryClient.getQueryState` now accepts queryKey only as an Argument + +`queryClient.getQueryState` argument is changed to accept only a `queryKey` + +```diff +- queryClient.getQueryState(queryKey, filters) ++ queryClient.getQueryState(queryKey) +``` + ### The `remove` method has been removed from useQuery Previously, remove method used to remove the query from the queryCache without informing observers about it. It was best used to remove data imperatively that is no longer needed, e.g. when logging a user out. diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index a253ab5e21..de01824703 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -172,7 +172,7 @@ const data = queryClient.getQueryData(queryKey) **Options** -- `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters) +- `queryKey: QueryKey`: [Query Keys](../guides/query-keys) **Returns** @@ -263,13 +263,13 @@ Updates via `setQueryData` must be performed in an _immuatable_ way. **DO NOT** `getQueryState` is a synchronous function that can be used to get an existing query's state. If the query does not exist, `undefined` will be returned. ```tsx -const state = queryClient.getQueryState({ queryKey }) +const state = queryClient.getQueryState(queryKey) console.log(state.dataUpdatedAt) ``` **Options** -- `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters) +- `queryKey: QueryKey`: [Query Keys](../guides/query-keys) ## `queryClient.setQueriesData` diff --git a/docs/solid/overview.md b/docs/solid/overview.md index b2f59dd93b..ecb6bb20a0 100644 --- a/docs/solid/overview.md +++ b/docs/solid/overview.md @@ -14,7 +14,10 @@ import { Switch, Match, For } from 'solid-js' const queryClient = new QueryClient() function Example() { - const query = createQuery(() => ['todos'], fetchTodos) + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos + }) return (
@@ -70,10 +73,16 @@ Solid Query offers an API similar to React Query, but there are some key differ ```tsx // ❌ react version -useQuery(["todos", todo], fetchTodos) +useQuery({ + queryKey: ["todos", todo], + queryFn: fetchTodos +}) // ✅ solid version -createQuery(() => ["todos", todo()], fetchTodos) +createQuery({ + queryKey: () => ["todos", todo()], + queryFn: fetchTodos +}) ``` - Suspense works for queries out of the box if you access the query data inside a `` boundary. @@ -82,7 +91,11 @@ createQuery(() => ["todos", todo()], fetchTodos) import { For, Suspense } from 'solid-js' function Example() { - const query = createQuery(() => ['todos'], fetchTodos) + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos + } + ) return (
{/* ✅ Will trigger loading fallback, data accessed in a suspense context. */} @@ -114,20 +127,21 @@ export default function App() { function Example() { // ❌ react version -- supports destructing outside reactive context - // const { isLoading, error, data } = useQuery(['repoData'], () => - // fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => + // const { isLoading, error, data } = useQuery({ + // queryKey: ['repoData'], () => + // queryFn: fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => // res.json() // ) - // ) + // }) // ✅ solid version -- does not support destructuring outside reactive context - const query = createQuery( - () => ['repoData'], - () => + const query = createQuery({ + queryKey: () => ['repoData'], + queryFn: () => fetch('https://api.github.com/repos/tannerlinsley/react-query').then( (res) => res.json(), ), - ) + }) // ✅ access query properties in JSX reactive context return ( @@ -162,7 +176,9 @@ const queryClient = new QueryClient() function Example() { const [enabled, setEnabled] = createSignal(false) - const query = createQuery(() => ['todos'], fetchTodos, { + const query = createQuery({ + queryKey: () => ['todos'], + queryFn: fetchTodos, // ❌ passing a signal directly is not reactive // enabled: enabled(), diff --git a/examples/react/load-more-infinite-scroll/pages/index.js b/examples/react/load-more-infinite-scroll/pages/index.js index 1a43fca870..495c514a92 100755 --- a/examples/react/load-more-infinite-scroll/pages/index.js +++ b/examples/react/load-more-infinite-scroll/pages/index.js @@ -33,17 +33,15 @@ function Example() { fetchPreviousPage, hasNextPage, hasPreviousPage, - } = useInfiniteQuery( - ['projects'], - async ({ pageParam = 0 }) => { + } = useInfiniteQuery({ + queryKey: ['projects'], + queryFn: async ({ pageParam = 0 }) => { const res = await axios.get('/api/projects?cursor=' + pageParam) return res.data }, - { - getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined, - getNextPageParam: (lastPage) => lastPage.nextId ?? undefined, - }, - ) + getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined, + getNextPageParam: (lastPage) => lastPage.nextId ?? undefined, + }) React.useEffect(() => { if (inView) { diff --git a/examples/react/react-native/src/screens/MovieDetailsScreen.tsx b/examples/react/react-native/src/screens/MovieDetailsScreen.tsx index 1a66705051..e19a657bc0 100644 --- a/examples/react/react-native/src/screens/MovieDetailsScreen.tsx +++ b/examples/react/react-native/src/screens/MovieDetailsScreen.tsx @@ -22,11 +22,11 @@ type Props = { }; export function MovieDetailsScreen({ route }: Props) { - const { isLoading, error, data, refetch } = useQuery( - ['movie', route.params.movie.title], - () => fetchMovie(route.params.movie.title), - { initialData: route.params.movie as MovieDetails } - ); + const { isLoading, error, data, refetch } = useQuery({ + queryKey: ['movie', route.params.movie.title], + queryFn: () => fetchMovie(route.params.movie.title), + initialData: route.params.movie as MovieDetails, + }); const { isRefetchingByUser, refetchByUser } = useRefreshByUser(refetch); diff --git a/examples/react/react-native/src/screens/MoviesListScreen.tsx b/examples/react/react-native/src/screens/MoviesListScreen.tsx index e5f94720d8..89b1f07d61 100644 --- a/examples/react/react-native/src/screens/MoviesListScreen.tsx +++ b/examples/react/react-native/src/screens/MoviesListScreen.tsx @@ -22,10 +22,10 @@ type Props = { }; export function MoviesListScreen({ navigation }: Props) { - const { isLoading, error, data, refetch } = useQuery( - ['movies'], - fetchMovies - ); + const { isLoading, error, data, refetch } = useQuery({ + queryKey: ['movies'], + queryFn: fetchMovies, + }); const { isRefetchingByUser, refetchByUser } = useRefreshByUser(refetch); useRefreshOnFocus(refetch); diff --git a/examples/react/react-router/src/routes/root.jsx b/examples/react/react-router/src/routes/root.jsx index 098fcc850b..0ced904091 100644 --- a/examples/react/react-router/src/routes/root.jsx +++ b/examples/react/react-router/src/routes/root.jsx @@ -37,7 +37,7 @@ export const action = (queryClient) => async () => { export default function Root() { const { q } = useLoaderData(); const { data: contacts } = useQuery(contactListQuery(q)); - const searching = useIsFetching(["contacts", "list"]) > 0; + const searching = useIsFetching({ queryKey: ["contacts", "list"] }) > 0; const navigation = useNavigation(); const submit = useSubmit(); diff --git a/examples/solid/basic-typescript/src/index.tsx b/examples/solid/basic-typescript/src/index.tsx index 9de887054d..502ccaa7c7 100644 --- a/examples/solid/basic-typescript/src/index.tsx +++ b/examples/solid/basic-typescript/src/index.tsx @@ -97,13 +97,11 @@ const getPostById = async (id: number): Promise => { } function createPost(postId: number) { - return createQuery( - () => ['post', postId], - () => getPostById(postId), - { - enabled: !!postId, - }, - ) + return createQuery({ + queryKey: () => ['post', postId], + queryFn: () => getPostById(postId), + enabled: !!postId, + }) } function Post(props: { postId: number; setPostId: Setter }) { diff --git a/examples/vue/dependent-queries/src/Post.vue b/examples/vue/dependent-queries/src/Post.vue index 33b0c7b1d0..064d4c449c 100644 --- a/examples/vue/dependent-queries/src/Post.vue +++ b/examples/vue/dependent-queries/src/Post.vue @@ -30,17 +30,18 @@ export default defineComponent({ isFetching, data: post, error, - } = useQuery(['post', props.postId], () => fetchPost(props.postId)) + } = useQuery({ + queryKey: ['post', props.postId], + queryFn: () => fetchPost(props.postId), + }) const authorId = computed(() => post.value?.userId) - const { data: author } = useQuery( - ['author', authorId], - () => fetchAuthor(authorId.value), - { - enabled: computed(() => !!post.value?.userId), - }, - ) + const { data: author } = useQuery({ + queryKey: ['author', authorId], + queryFn: () => fetchAuthor(authorId.value), + enabled: computed(() => !!post.value?.userId), + }) return { isLoading, isError, isFetching, post, error, author } }, diff --git a/examples/vue/dependent-queries/src/Posts.vue b/examples/vue/dependent-queries/src/Posts.vue index fb21a55ed0..ad63680a73 100644 --- a/examples/vue/dependent-queries/src/Posts.vue +++ b/examples/vue/dependent-queries/src/Posts.vue @@ -19,10 +19,10 @@ export default defineComponent({ }, emits: ['setPostId'], setup() { - const { isLoading, isError, isFetching, data, error, refetch } = useQuery( - ['posts'], - fetcher, - ) + const { isLoading, isError, isFetching, data, error, refetch } = useQuery({ + queryKey: ['posts'], + queryFn: fetcher, + }) return { isLoading, isError, isFetching, data, error, refetch } }, diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index 38a3949aa4..d2bee42dd8 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -11,16 +11,7 @@ export { MutationObserver } from './mutationObserver' export { notifyManager } from './notifyManager' export { focusManager } from './focusManager' export { onlineManager } from './onlineManager' -export { - hashQueryKey, - replaceEqualDeep, - isError, - isServer, - parseQueryArgs, - parseFilterArgs, - parseMutationFilterArgs, - parseMutationArgs, -} from './utils' +export { hashQueryKey, replaceEqualDeep, isError, isServer } from './utils' export type { MutationFilters, QueryFilters, Updater } from './utils' export { isCancelledError } from './retryer' export { dehydrate, hydrate } from './hydration' diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index e3c0690bb8..78dad1b87d 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -1,8 +1,8 @@ import type { QueryFilters } from './utils' -import { hashQueryKeyByOptions, matchQuery, parseFilterArgs } from './utils' +import { hashQueryKeyByOptions, matchQuery } from './utils' import type { Action, QueryState } from './query' import { Query } from './query' -import type { QueryKey, QueryOptions } from './types' +import type { QueryKey, QueryOptions, WithRequired } from './types' import { notifyManager } from './notifyManager' import type { QueryClient } from './queryClient' import { Subscribable } from './subscribable' @@ -161,11 +161,8 @@ export class QueryCache extends Subscribable { } find( - arg1: QueryKey, - arg2?: QueryFilters, + filters: WithRequired, ): Query | undefined { - const [filters] = parseFilterArgs(arg1, arg2) - if (typeof filters.exact === 'undefined') { filters.exact = true } @@ -173,11 +170,7 @@ export class QueryCache extends Subscribable { return this.queries.find((query) => matchQuery(filters, query)) } - findAll(queryKey?: QueryKey, filters?: QueryFilters): Query[] - findAll(filters?: QueryFilters): Query[] - findAll(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): Query[] - findAll(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): Query[] { - const [filters] = parseFilterArgs(arg1, arg2) + findAll(filters: QueryFilters = {}): Query[] { return Object.keys(filters).length > 0 ? this.queries.filter((query) => matchQuery(filters, query)) : this.queries diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 4f0ce0540c..812a42e0e5 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -2,8 +2,6 @@ import type { QueryFilters, Updater, MutationFilters } from './utils' import { hashQueryKey, noop, - parseFilterArgs, - parseQueryArgs, partialMatchKey, hashQueryKeyByOptions, functionalUpdate, @@ -19,7 +17,6 @@ import type { MutationKey, MutationObserverOptions, MutationOptions, - QueryFunction, QueryKey, QueryObserverOptions, QueryOptions, @@ -28,7 +25,6 @@ import type { ResetOptions, ResetQueryFilters, SetDataOptions, - WithRequired, } from './types' import type { QueryState } from './query' import { QueryCache } from './queryCache' @@ -100,10 +96,7 @@ export class QueryClient { this.unsubscribeOnline?.() } - isFetching(filters?: QueryFilters): number - isFetching(queryKey?: QueryKey, filters?: QueryFilters): number - isFetching(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): number { - const [filters] = parseFilterArgs(arg1, arg2) + isFetching(filters: QueryFilters = {}): number { filters.fetchStatus = 'fetching' return this.queryCache.findAll(filters).length } @@ -114,83 +107,28 @@ export class QueryClient { getQueryData( queryKey: QueryKey, - filters?: QueryFilters, ): TQueryFnData | undefined { - return this.queryCache.find(queryKey, filters)?.state.data + return this.queryCache.find({ queryKey })?.state.data } - ensureQueryData< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - options: WithRequired< - FetchQueryOptions, - 'queryKey' - >, - ): Promise - ensureQueryData< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - options?: Omit< - FetchQueryOptions, - 'queryKey' - >, - ): Promise - ensureQueryData< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: Omit< - FetchQueryOptions, - 'queryKey' | 'queryFn' - >, - ): Promise ensureQueryData< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | TQueryKey - | WithRequired< - FetchQueryOptions, - 'queryKey' - >, - arg2?: - | QueryFunction - | FetchQueryOptions, - arg3?: FetchQueryOptions, + options: FetchQueryOptions, ): Promise { - const parsedOptions = parseQueryArgs(arg1, arg2, arg3) - const cachedData = this.getQueryData(parsedOptions.queryKey!) + const cachedData = this.getQueryData(options.queryKey) - return cachedData - ? Promise.resolve(cachedData) - : this.fetchQuery(parsedOptions) + return cachedData ? Promise.resolve(cachedData) : this.fetchQuery(options) } - getQueriesData( - queryKey: QueryKey, - ): [QueryKey, TQueryFnData | undefined][] getQueriesData( filters: QueryFilters, - ): [QueryKey, TQueryFnData | undefined][] - getQueriesData( - queryKeyOrFilters: QueryKey | QueryFilters, ): [QueryKey, TQueryFnData | undefined][] { return this.getQueryCache() - .findAll(queryKeyOrFilters) + .findAll(filters) .map(({ queryKey, state }) => { const data = state.data as TQueryFnData | undefined return [queryKey, data] @@ -202,7 +140,7 @@ export class QueryClient { updater: Updater, options?: SetDataOptions, ): TQueryFnData | undefined { - const query = this.queryCache.find(queryKey) + const query = this.queryCache.find({ queryKey }) const prevData = query?.state.data const data = functionalUpdate(updater, prevData) @@ -210,33 +148,27 @@ export class QueryClient { return undefined } - const parsedOptions = parseQueryArgs(queryKey) - const defaultedOptions = this.defaultQueryOptions(parsedOptions) + const defaultedOptions = this.defaultQueryOptions< + any, + any, + unknown, + any, + QueryKey + >({ queryKey }) + return this.queryCache .build(this, defaultedOptions) .setData(data, { ...options, manual: true }) } - setQueriesData( - queryKey: QueryKey, - updater: Updater, - options?: SetDataOptions, - ): [QueryKey, TQueryFnData | undefined][] - setQueriesData( filters: QueryFilters, updater: Updater, options?: SetDataOptions, - ): [QueryKey, TQueryFnData | undefined][] - - setQueriesData( - queryKeyOrFilters: QueryKey | QueryFilters, - updater: Updater, - options?: SetDataOptions, ): [QueryKey, TQueryFnData | undefined][] { return notifyManager.batch(() => this.getQueryCache() - .findAll(queryKeyOrFilters) + .findAll(filters) .map(({ queryKey }) => [ queryKey, this.setQueryData(queryKey, updater, options), @@ -246,15 +178,11 @@ export class QueryClient { getQueryState( queryKey: QueryKey, - filters?: QueryFilters, ): QueryState | undefined { - return this.queryCache.find(queryKey, filters)?.state + return this.queryCache.find({ queryKey })?.state } - removeQueries(filters?: QueryFilters): void - removeQueries(queryKey?: QueryKey, filters?: QueryFilters): void - removeQueries(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): void { - const [filters] = parseFilterArgs(arg1, arg2) + removeQueries(filters?: QueryFilters): void { const queryCache = this.queryCache notifyManager.batch(() => { queryCache.findAll(filters).forEach((query) => { @@ -266,21 +194,10 @@ export class QueryClient { resetQueries( filters?: ResetQueryFilters, options?: ResetOptions, - ): Promise - resetQueries( - queryKey?: QueryKey, - filters?: ResetQueryFilters, - options?: ResetOptions, - ): Promise - resetQueries( - arg1?: QueryKey | ResetQueryFilters, - arg2?: ResetQueryFilters | ResetOptions, - arg3?: ResetOptions, ): Promise { - const [filters, options] = parseFilterArgs(arg1, arg2, arg3) const queryCache = this.queryCache - const refetchFilters: RefetchQueryFilters = { + const refetchFilters: RefetchQueryFilters = { type: 'active', ...filters, } @@ -293,19 +210,10 @@ export class QueryClient { }) } - cancelQueries(filters?: QueryFilters, options?: CancelOptions): Promise - cancelQueries( - queryKey?: QueryKey, - filters?: QueryFilters, - options?: CancelOptions, - ): Promise cancelQueries( - arg1?: QueryKey | QueryFilters, - arg2?: QueryFilters | CancelOptions, - arg3?: CancelOptions, + filters: QueryFilters = {}, + cancelOptions: CancelOptions = {}, ): Promise { - const [filters, cancelOptions = {}] = parseFilterArgs(arg1, arg2, arg3) - if (typeof cancelOptions.revert === 'undefined') { cancelOptions.revert = true } @@ -320,21 +228,9 @@ export class QueryClient { } invalidateQueries( - filters?: InvalidateQueryFilters, - options?: InvalidateOptions, - ): Promise - invalidateQueries( - queryKey?: QueryKey, - filters?: InvalidateQueryFilters, - options?: InvalidateOptions, - ): Promise - invalidateQueries( - arg1?: QueryKey | InvalidateQueryFilters, - arg2?: InvalidateQueryFilters | InvalidateOptions, - arg3?: InvalidateOptions, + filters: InvalidateQueryFilters = {}, + options: InvalidateOptions = {}, ): Promise { - const [filters, options] = parseFilterArgs(arg1, arg2, arg3) - return notifyManager.batch(() => { this.queryCache.findAll(filters).forEach((query) => { query.invalidate() @@ -343,7 +239,7 @@ export class QueryClient { if (filters.refetchType === 'none') { return Promise.resolve() } - const refetchFilters: RefetchQueryFilters = { + const refetchFilters: RefetchQueryFilters = { ...filters, type: filters.refetchType ?? filters.type ?? 'active', } @@ -352,21 +248,9 @@ export class QueryClient { } refetchQueries( - filters?: RefetchQueryFilters, + filters: RefetchQueryFilters = {}, options?: RefetchOptions, - ): Promise - refetchQueries( - queryKey?: QueryKey, - filters?: RefetchQueryFilters, - options?: RefetchOptions, - ): Promise - refetchQueries( - arg1?: QueryKey | RefetchQueryFilters, - arg2?: RefetchQueryFilters | RefetchOptions, - arg3?: RefetchOptions, ): Promise { - const [filters, options] = parseFilterArgs(arg1, arg2, arg3) - const promises = notifyManager.batch(() => this.queryCache .findAll(filters) @@ -389,47 +273,15 @@ export class QueryClient { return promise } - fetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - options: FetchQueryOptions, - ): Promise - fetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - options?: FetchQueryOptions, - ): Promise - fetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: FetchQueryOptions, - ): Promise fetchQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: TQueryKey | FetchQueryOptions, - arg2?: - | QueryFunction - | FetchQueryOptions, - arg3?: FetchQueryOptions, + options: FetchQueryOptions, ): Promise { - const parsedOptions = parseQueryArgs(arg1, arg2, arg3) - const defaultedOptions = this.defaultQueryOptions(parsedOptions) + const defaultedOptions = this.defaultQueryOptions(options) // https://github.com/tannerlinsley/react-query/issues/652 if (typeof defaultedOptions.retry === 'undefined') { @@ -450,137 +302,31 @@ export class QueryClient { TQueryKey extends QueryKey = QueryKey, >( options: FetchQueryOptions, - ): Promise - prefetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - options?: FetchQueryOptions, - ): Promise - prefetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: FetchQueryOptions, - ): Promise - prefetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - arg1: TQueryKey | FetchQueryOptions, - arg2?: - | QueryFunction - | FetchQueryOptions, - arg3?: FetchQueryOptions, ): Promise { - return this.fetchQuery(arg1 as any, arg2 as any, arg3) - .then(noop) - .catch(noop) + return this.fetchQuery(options).then(noop).catch(noop) } - fetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - options: FetchInfiniteQueryOptions, - ): Promise> - fetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - options?: FetchInfiniteQueryOptions, - ): Promise> - fetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: FetchInfiniteQueryOptions, - ): Promise> fetchInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | TQueryKey - | FetchInfiniteQueryOptions, - arg2?: - | QueryFunction - | FetchInfiniteQueryOptions, - arg3?: FetchInfiniteQueryOptions, + options: FetchInfiniteQueryOptions, ): Promise> { - const parsedOptions = parseQueryArgs(arg1, arg2, arg3) - parsedOptions.behavior = infiniteQueryBehavior< - TQueryFnData, - TError, - TData - >() - return this.fetchQuery(parsedOptions) + options.behavior = infiniteQueryBehavior() + return this.fetchQuery(options) } - prefetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - options: FetchInfiniteQueryOptions, - ): Promise - prefetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - options?: FetchInfiniteQueryOptions, - ): Promise - prefetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: FetchInfiniteQueryOptions, - ): Promise prefetchInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | TQueryKey - | FetchInfiniteQueryOptions, - arg2?: - | QueryFunction - | FetchInfiniteQueryOptions, - arg3?: FetchInfiniteQueryOptions, + options: FetchInfiniteQueryOptions, ): Promise { - return this.fetchInfiniteQuery(arg1 as any, arg2 as any, arg3) - .then(noop) - .catch(noop) + return this.fetchInfiniteQuery(options).then(noop).catch(noop) } resumePausedMutations(): Promise { @@ -609,7 +355,7 @@ export class QueryClient { setQueryDefaults( queryKey: QueryKey, - options: QueryObserverOptions, + options: Omit, 'queryKey'>, ): void { const result = this.queryDefaults.find( (x) => hashQueryKey(queryKey) === hashQueryKey(x.queryKey), diff --git a/packages/query-core/src/tests/hydration.test.tsx b/packages/query-core/src/tests/hydration.test.tsx index 1debd880b6..5b6601914e 100644 --- a/packages/query-core/src/tests/hydration.test.tsx +++ b/packages/query-core/src/tests/hydration.test.tsx @@ -16,14 +16,30 @@ describe('dehydration and rehydration', () => { test('should work with serializeable values', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => fetchData('string')) - await queryClient.prefetchQuery(['number'], () => fetchData(1)) - await queryClient.prefetchQuery(['boolean'], () => fetchData(true)) - await queryClient.prefetchQuery(['null'], () => fetchData(null)) - await queryClient.prefetchQuery(['array'], () => fetchData(['string', 0])) - await queryClient.prefetchQuery(['nested'], () => - fetchData({ key: [{ nestedKey: 1 }] }), - ) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string'), + }) + await queryClient.prefetchQuery({ + queryKey: ['number'], + queryFn: () => fetchData(1), + }) + await queryClient.prefetchQuery({ + queryKey: ['boolean'], + queryFn: () => fetchData(true), + }) + await queryClient.prefetchQuery({ + queryKey: ['null'], + queryFn: () => fetchData(null), + }) + await queryClient.prefetchQuery({ + queryKey: ['array'], + queryFn: () => fetchData(['string', 0]), + }) + await queryClient.prefetchQuery({ + queryKey: ['nested'], + queryFn: () => fetchData({ key: [{ nestedKey: 1 }] }), + }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) @@ -35,32 +51,51 @@ describe('dehydration and rehydration', () => { queryCache: hydrationCache, }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['string'])?.state.data).toBe('string') - expect(hydrationCache.find(['number'])?.state.data).toBe(1) - expect(hydrationCache.find(['boolean'])?.state.data).toBe(true) - expect(hydrationCache.find(['null'])?.state.data).toBe(null) - expect(hydrationCache.find(['array'])?.state.data).toEqual(['string', 0]) - expect(hydrationCache.find(['nested'])?.state.data).toEqual({ + expect(hydrationCache.find({ queryKey: ['string'] })?.state.data).toBe( + 'string', + ) + expect(hydrationCache.find({ queryKey: ['number'] })?.state.data).toBe(1) + expect(hydrationCache.find({ queryKey: ['boolean'] })?.state.data).toBe( + true, + ) + expect(hydrationCache.find({ queryKey: ['null'] })?.state.data).toBe(null) + expect(hydrationCache.find({ queryKey: ['array'] })?.state.data).toEqual([ + 'string', + 0, + ]) + expect(hydrationCache.find({ queryKey: ['nested'] })?.state.data).toEqual({ key: [{ nestedKey: 1 }], }) const fetchDataAfterHydration = jest.fn() - await hydrationClient.prefetchQuery(['string'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['string'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) - await hydrationClient.prefetchQuery(['number'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['number'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) - await hydrationClient.prefetchQuery(['boolean'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['boolean'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) - await hydrationClient.prefetchQuery(['null'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['null'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) - await hydrationClient.prefetchQuery(['array'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['array'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) - await hydrationClient.prefetchQuery(['nested'], fetchDataAfterHydration, { + await hydrationClient.prefetchQuery({ + queryKey: ['nested'], + queryFn: fetchDataAfterHydration, staleTime: 1000, }) expect(fetchDataAfterHydration).toHaveBeenCalledTimes(0) @@ -72,7 +107,10 @@ describe('dehydration and rehydration', () => { test('should not dehydrate queries if dehydrateQueries is set to false', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => fetchData('string')) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string'), + }) const dehydrated = dehydrate(queryClient, { dehydrateQueries: false }) @@ -84,7 +122,9 @@ describe('dehydration and rehydration', () => { test('should use the cache time from the client', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => fetchData('string'), { + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string'), cacheTime: 50, }) const dehydrated = dehydrate(queryClient) @@ -98,9 +138,11 @@ describe('dehydration and rehydration', () => { const hydrationCache = new QueryCache() const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['string'])?.state.data).toBe('string') + expect(hydrationCache.find({ queryKey: ['string'] })?.state.data).toBe( + 'string', + ) await sleep(100) - expect(hydrationCache.find(['string'])).toBeTruthy() + expect(hydrationCache.find({ queryKey: ['string'] })).toBeTruthy() queryClient.clear() hydrationClient.clear() @@ -109,7 +151,10 @@ describe('dehydration and rehydration', () => { test('should be able to provide default options for the hydrated queries', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => fetchData('string')) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string'), + }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) const parsed = JSON.parse(stringified) @@ -118,7 +163,9 @@ describe('dehydration and rehydration', () => { hydrate(hydrationClient, parsed, { defaultOptions: { queries: { retry: 10 } }, }) - expect(hydrationCache.find(['string'])?.options.retry).toBe(10) + expect(hydrationCache.find({ queryKey: ['string'] })?.options.retry).toBe( + 10, + ) queryClient.clear() hydrationClient.clear() }) @@ -126,10 +173,10 @@ describe('dehydration and rehydration', () => { test('should work with complex keys', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery( - ['string', { key: ['string'], key2: 0 }], - () => fetchData('string'), - ) + await queryClient.prefetchQuery({ + queryKey: ['string', { key: ['string'], key2: 0 }], + queryFn: () => fetchData('string'), + }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) @@ -140,15 +187,17 @@ describe('dehydration and rehydration', () => { const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) expect( - hydrationCache.find(['string', { key: ['string'], key2: 0 }])?.state.data, + hydrationCache.find({ + queryKey: ['string', { key: ['string'], key2: 0 }], + })?.state.data, ).toBe('string') const fetchDataAfterHydration = jest.fn() - await hydrationClient.prefetchQuery( - ['string', { key: ['string'], key2: 0 }], - fetchDataAfterHydration, - { staleTime: 100 }, - ) + await hydrationClient.prefetchQuery({ + queryKey: ['string', { key: ['string'], key2: 0 }], + queryFn: fetchDataAfterHydration, + staleTime: 100, + }) expect(fetchDataAfterHydration).toHaveBeenCalledTimes(0) queryClient.clear() @@ -161,10 +210,19 @@ describe('dehydration and rehydration', () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['success'], () => fetchData('success')) - queryClient.prefetchQuery(['loading'], () => fetchData('loading', 10000)) - await queryClient.prefetchQuery(['error'], () => { - throw new Error() + await queryClient.prefetchQuery({ + queryKey: ['success'], + queryFn: () => fetchData('success'), + }) + queryClient.prefetchQuery({ + queryKey: ['loading'], + queryFn: () => fetchData('loading', 10000), + }) + await queryClient.prefetchQuery({ + queryKey: ['error'], + queryFn: () => { + throw new Error() + }, }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) @@ -176,9 +234,9 @@ describe('dehydration and rehydration', () => { const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['success'])).toBeTruthy() - expect(hydrationCache.find(['loading'])).toBeFalsy() - expect(hydrationCache.find(['error'])).toBeFalsy() + expect(hydrationCache.find({ queryKey: ['success'] })).toBeTruthy() + expect(hydrationCache.find({ queryKey: ['loading'] })).toBeFalsy() + expect(hydrationCache.find({ queryKey: ['error'] })).toBeFalsy() queryClient.clear() hydrationClient.clear() @@ -188,8 +246,14 @@ describe('dehydration and rehydration', () => { test('should filter queries via shouldDehydrateQuery', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => fetchData('string')) - await queryClient.prefetchQuery(['number'], () => fetchData(1)) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string'), + }) + await queryClient.prefetchQuery({ + queryKey: ['number'], + queryFn: () => fetchData(1), + }) const dehydrated = dehydrate(queryClient, { shouldDehydrateQuery: (query) => query.queryKey[0] !== 'string', }) @@ -209,8 +273,8 @@ describe('dehydration and rehydration', () => { const hydrationCache = new QueryCache() const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['string'])).toBeUndefined() - expect(hydrationCache.find(['number'])?.state.data).toBe(1) + expect(hydrationCache.find({ queryKey: ['string'] })).toBeUndefined() + expect(hydrationCache.find({ queryKey: ['number'] })?.state.data).toBe(1) queryClient.clear() hydrationClient.clear() @@ -219,9 +283,10 @@ describe('dehydration and rehydration', () => { test('should not overwrite query in cache if hydrated query is older', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => - fetchData('string-older', 5), - ) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string-older', 5), + }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) @@ -230,12 +295,15 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() const hydrationClient = createQueryClient({ queryCache: hydrationCache }) - await hydrationClient.prefetchQuery(['string'], () => - fetchData('string-newer', 5), - ) + await hydrationClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string-newer', 5), + }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer') + expect(hydrationCache.find({ queryKey: ['string'] })?.state.data).toBe( + 'string-newer', + ) queryClient.clear() hydrationClient.clear() @@ -244,17 +312,19 @@ describe('dehydration and rehydration', () => { test('should overwrite query in cache if hydrated query is newer', async () => { const hydrationCache = new QueryCache() const hydrationClient = createQueryClient({ queryCache: hydrationCache }) - await hydrationClient.prefetchQuery(['string'], () => - fetchData('string-older', 5), - ) + await hydrationClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string-older', 5), + }) // --- const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => - fetchData('string-newer', 5), - ) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => fetchData('string-newer', 5), + }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) @@ -262,7 +332,9 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) hydrate(hydrationClient, parsed) - expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer') + expect(hydrationCache.find({ queryKey: ['string'] })?.state.data).toBe( + 'string-newer', + ) queryClient.clear() hydrationClient.clear() diff --git a/packages/query-core/src/tests/queriesObserver.test.tsx b/packages/query-core/src/tests/queriesObserver.test.tsx index 5be415ae03..e16c6d5de8 100644 --- a/packages/query-core/src/tests/queriesObserver.test.tsx +++ b/packages/query-core/src/tests/queriesObserver.test.tsx @@ -122,11 +122,11 @@ describe('queriesObserver', () => { observer.setQueries([{ queryKey: key2, queryFn: queryFn2 }]) await sleep(1) const queryCache = queryClient.getQueryCache() - expect(queryCache.find(key1, { type: 'active' })).toBeUndefined() - expect(queryCache.find(key2, { type: 'active' })).toBeDefined() + expect(queryCache.find({ queryKey: key1, type: 'active' })).toBeUndefined() + expect(queryCache.find({ queryKey: key2, type: 'active' })).toBeDefined() unsubscribe() - expect(queryCache.find(key1, { type: 'active' })).toBeUndefined() - expect(queryCache.find(key2, { type: 'active' })).toBeUndefined() + expect(queryCache.find({ queryKey: key1, type: 'active' })).toBeUndefined() + expect(queryCache.find({ queryKey: key2, type: 'active' })).toBeUndefined() expect(results.length).toBe(6) expect(results[0]).toMatchObject([ { status: 'loading', fetchStatus: 'idle', data: undefined }, diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index d2ed06dc1c..64c87be01a 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -30,16 +30,22 @@ describe('query', () => { test('should use the longest cache time it has seen', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data', { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', cacheTime: 100, }) - await queryClient.prefetchQuery(key, () => 'data', { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', cacheTime: 200, }) - await queryClient.prefetchQuery(key, () => 'data', { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', cacheTime: 10, }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.cacheTime).toBe(200) }) @@ -52,9 +58,9 @@ describe('query', () => { let count = 0 let result - const promise = queryClient.fetchQuery( - key, - async () => { + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn: async () => { count++ if (count === 3) { @@ -63,11 +69,9 @@ describe('query', () => { throw new Error(`error${count}`) }, - { - retry: 3, - retryDelay: 1, - }, - ) + retry: 3, + retryDelay: 1, + }) promise.then((data) => { result = data @@ -100,9 +104,9 @@ describe('query', () => { let count = 0 let result - const promise = queryClient.fetchQuery( - key, - async () => { + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn: async () => { count++ if (count === 3) { @@ -111,11 +115,9 @@ describe('query', () => { throw new Error(`error${count}`) }, - { - retry: 3, - retryDelay: 1, - }, - ) + retry: 3, + retryDelay: 1, + }) promise.then((data) => { result = data @@ -148,23 +150,21 @@ describe('query', () => { let count = 0 let result - const promise = queryClient.fetchQuery( - key, - async (): Promise => { + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn: async (): Promise => { count++ throw new Error(`error${count}`) }, - { - retry: 3, - retryDelay: 1, - }, - ) + retry: 3, + retryDelay: 1, + }) promise.catch((data) => { result = data }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! // Check if the query is really paused await sleep(50) @@ -195,7 +195,7 @@ describe('query', () => { >() .mockResolvedValue('data') - queryClient.prefetchQuery(key, queryFn) + queryClient.prefetchQuery({ queryKey: key, queryFn }) await sleep(10) @@ -210,9 +210,12 @@ describe('query', () => { test('should continue if cancellation is not supported and signal is not consumed', async () => { const key = queryKey() - queryClient.prefetchQuery(key, async () => { - await sleep(100) - return 'data' + queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => { + await sleep(100) + return 'data' + }, }) await sleep(10) @@ -227,7 +230,7 @@ describe('query', () => { await sleep(100) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.state).toMatchObject({ data: 'data', @@ -239,9 +242,12 @@ describe('query', () => { test('should not continue when last observer unsubscribed if the signal was consumed', async () => { const key = queryKey() - queryClient.prefetchQuery(key, async ({ signal }) => { - await sleep(100) - return signal?.aborted ? 'aborted' : 'data' + queryClient.prefetchQuery({ + queryKey: key, + queryFn: async ({ signal }) => { + await sleep(100) + return signal?.aborted ? 'aborted' : 'data' + }, }) await sleep(10) @@ -256,7 +262,7 @@ describe('query', () => { await sleep(100) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.state).toMatchObject({ data: undefined, @@ -289,7 +295,9 @@ describe('query', () => { throw new Error() }) - const promise = queryClient.fetchQuery(key, queryFn, { + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn, retry: 3, retryDelay: 10, }) @@ -298,7 +306,7 @@ describe('query', () => { error = e }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(queryFn).toHaveBeenCalledTimes(1) @@ -329,7 +337,9 @@ describe('query', () => { let error - const promise = queryClient.fetchQuery(key, queryFn, { + const promise = queryClient.fetchQuery({ + queryKey: key, + queryFn, retry: 3, retryDelay: 10, }) @@ -338,7 +348,7 @@ describe('query', () => { error = e }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! query.cancel() await sleep(100) @@ -357,13 +367,10 @@ describe('query', () => { throw new Error() }) - queryClient.fetchQuery(key, queryFn, { - retry: 3, - retryDelay: 10, - }) + queryClient.fetchQuery({ queryKey: key, queryFn, retry: 3, retryDelay: 10 }) // Ensure the query is loading - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.state.status).toBe('loading') // Reset the query while it is loading @@ -387,8 +394,8 @@ describe('query', () => { return 'data' }) - queryClient.prefetchQuery(key, queryFn) - const query = queryCache.find(key)! + queryClient.prefetchQuery({ queryKey: key, queryFn }) + const query = queryCache.find({ queryKey: key })! await sleep(10) query.cancel() await sleep(100) @@ -403,8 +410,11 @@ describe('query', () => { test('cancelling a resolved query should not have any effect', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, async () => 'data') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => 'data', + }) + const query = queryCache.find({ queryKey: key })! query.cancel() await sleep(10) expect(query.state.data).toBe('data') @@ -413,10 +423,13 @@ describe('query', () => { test('cancelling a rejected query should not have any effect', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, async (): Promise => { - throw new Error('error') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: async (): Promise => { + throw new Error('error') + }, }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! query.cancel() await sleep(10) @@ -427,27 +440,25 @@ describe('query', () => { test('the previous query status should be kept when refetching', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) + const query = queryCache.find({ queryKey: key })! expect(query.state.status).toBe('success') - await queryClient.prefetchQuery( - key, - () => Promise.reject('reject'), - { - retry: false, - }, - ) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.reject('reject'), + retry: false, + }) expect(query.state.status).toBe('error') - queryClient.prefetchQuery( - key, - async () => { + queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return Promise.reject('reject') }, - { retry: false }, - ) + retry: false, + }) expect(query.state.status).toBe('error') await sleep(100) @@ -468,11 +479,15 @@ describe('query', () => { }) const unsubscribe1 = observer.subscribe(() => undefined) unsubscribe1() - await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) + await waitFor(() => + expect(queryCache.find({ queryKey: key })).toBeUndefined(), + ) const unsubscribe2 = observer.subscribe(() => undefined) unsubscribe2() - await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) + await waitFor(() => + expect(queryCache.find({ queryKey: key })).toBeUndefined(), + ) expect(count).toBe(1) }) @@ -483,11 +498,13 @@ describe('query', () => { queryFn: async () => 'data', cacheTime: 0, }) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() unsubscribe() - await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) + await waitFor(() => + expect(queryCache.find({ queryKey: key })).toBeUndefined(), + ) }) test('should be garbage collected later when unsubscribed and query is fetching', async () => { @@ -502,15 +519,17 @@ describe('query', () => { }) const unsubscribe = observer.subscribe(() => undefined) await sleep(20) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() observer.refetch() unsubscribe() await sleep(10) // unsubscribe should not remove even though cacheTime has elapsed b/c query is still fetching - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(10) // should be removed after an additional staleTime wait - await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) + await waitFor(() => + expect(queryCache.find({ queryKey: key })).toBeUndefined(), + ) }) test('should not be garbage collected unless there are no subscribers', async () => { @@ -520,16 +539,16 @@ describe('query', () => { queryFn: async () => 'data', cacheTime: 0, }) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) await sleep(100) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() unsubscribe() await sleep(100) - expect(queryCache.find(key)).toBeUndefined() + expect(queryCache.find({ queryKey: key })).toBeUndefined() queryClient.setQueryData(key, 'data') await sleep(100) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() }) test('should return proper count of observers', async () => { @@ -538,7 +557,7 @@ describe('query', () => { const observer = new QueryObserver(queryClient, options) const observer2 = new QueryObserver(queryClient, options) const observer3 = new QueryObserver(queryClient, options) - const query = queryCache.find(key) + const query = queryCache.find({ queryKey: key }) expect(query?.getObserversCount()).toEqual(0) @@ -564,11 +583,13 @@ describe('query', () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data', { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', meta, }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.meta).toBe(meta) expect(query.options.meta).toBe(meta) @@ -582,15 +603,11 @@ describe('query', () => { const key = queryKey() const queryFn = () => 'data' - await queryClient.prefetchQuery(key, queryFn, { - meta, - }) + await queryClient.prefetchQuery({ queryKey: key, queryFn, meta }) - await queryClient.prefetchQuery(key, queryFn, { - meta: undefined, - }) + await queryClient.prefetchQuery({ queryKey: key, queryFn, meta: undefined }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.meta).toBeUndefined() expect(query.options.meta).toBeUndefined() @@ -606,9 +623,9 @@ describe('query', () => { queryClient.setQueryDefaults(key, { meta }) - await queryClient.prefetchQuery(key, queryFn) + await queryClient.prefetchQuery({ queryKey: key, queryFn }) - const query = queryCache.find(key)! + const query = queryCache.find({ queryKey: key })! expect(query.meta).toBe(meta) }) @@ -622,9 +639,7 @@ describe('query', () => { const key = queryKey() - await queryClient.prefetchQuery(key, queryFn, { - meta, - }) + await queryClient.prefetchQuery({ queryKey: key, queryFn, meta }) expect(queryFn).toBeCalledWith( expect.objectContaining({ @@ -655,8 +670,8 @@ describe('query', () => { test('should not add an existing observer', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) + const query = queryCache.find({ queryKey: key })! expect(query.getObserversCount()).toEqual(0) const observer = new QueryObserver(queryClient, { @@ -674,8 +689,8 @@ describe('query', () => { test('should not try to remove an observer that does not exist', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) + const query = queryCache.find({ queryKey: key })! const observer = new QueryObserver(queryClient, { queryKey: key, }) @@ -691,8 +706,8 @@ describe('query', () => { test('should not dispatch "invalidate" on invalidate() if already invalidated', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) + const query = queryCache.find({ queryKey: key })! query.invalidate() expect(query.state.isInvalidated).toBeTruthy() @@ -717,8 +732,8 @@ describe('query', () => { return 'data' } - await queryClient.prefetchQuery(key, queryFn) - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn }) + const query = queryCache.find({ queryKey: key })! const meta = { meta1: '1' } @@ -772,9 +787,12 @@ describe('query', () => { globalThis['AbortController'] = undefined let signalTest: any - await queryClient.prefetchQuery(key, ({ signal }) => { - signalTest = signal - return 'data' + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: ({ signal }) => { + signalTest = signal + return 'data' + }, }) expect(signalTest).toBeUndefined() @@ -836,8 +854,8 @@ describe('query', () => { return 'data' } - await queryClient.prefetchQuery(key, queryFn) - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn }) + const query = queryCache.find({ queryKey: key })! query.fetch({ queryKey: key, diff --git a/packages/query-core/src/tests/queryCache.test.tsx b/packages/query-core/src/tests/queryCache.test.tsx index 1cacf9896d..c7ae5bfdab 100644 --- a/packages/query-core/src/tests/queryCache.test.tsx +++ b/packages/query-core/src/tests/queryCache.test.tsx @@ -23,7 +23,7 @@ describe('queryCache', () => { const subscriber = jest.fn() const unsubscribe = queryCache.subscribe(subscriber) queryClient.setQueryData(key, 'foo') - const query = queryCache.find(key) + const query = queryCache.find({ queryKey: key }) await sleep(1) expect(subscriber).toHaveBeenCalledWith({ query, type: 'added' }) unsubscribe() @@ -33,7 +33,7 @@ describe('queryCache', () => { const key = queryKey() const callback = jest.fn() queryCache.subscribe(callback) - queryClient.prefetchQuery(key, () => 'data') + queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) await sleep(100) expect(callback).toHaveBeenCalled() }) @@ -76,8 +76,8 @@ describe('queryCache', () => { const key = queryKey() const callback = jest.fn() queryCache.subscribe(callback) - queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key) + queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) + const query = queryCache.find({ queryKey: key }) await sleep(100) expect(callback).toHaveBeenCalledWith({ query, type: 'added' }) }) @@ -86,7 +86,9 @@ describe('queryCache', () => { const key = queryKey() const callback = jest.fn() queryCache.subscribe(callback) - queryClient.prefetchQuery(key, () => 'data', { + queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', initialData: 'initial', }) await sleep(100) @@ -97,15 +99,15 @@ describe('queryCache', () => { describe('find', () => { test('find should filter correctly', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data1') - const query = queryCache.find(key)! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data1' }) + const query = queryCache.find({ queryKey: key })! expect(query).toBeDefined() }) test('find should filter correctly with exact set to false', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data1') - const query = queryCache.find(key, { exact: false })! + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data1' }) + const query = queryCache.find({ queryKey: key, exact: false })! expect(query).toBeDefined() }) }) @@ -115,65 +117,96 @@ describe('queryCache', () => { const key1 = queryKey() const key2 = queryKey() const keyFetching = queryKey() - await queryClient.prefetchQuery(key1, () => 'data1') - await queryClient.prefetchQuery(key2, () => 'data2') - await queryClient.prefetchQuery([{ a: 'a', b: 'b' }], () => 'data3') - await queryClient.prefetchQuery(['posts', 1], () => 'data4') - queryClient.invalidateQueries(key2) - const query1 = queryCache.find(key1)! - const query2 = queryCache.find(key2)! - const query3 = queryCache.find([{ a: 'a', b: 'b' }])! - const query4 = queryCache.find(['posts', 1])! - - expect(queryCache.findAll(key1)).toEqual([query1]) + await queryClient.prefetchQuery({ + queryKey: key1, + queryFn: () => 'data1', + }) + await queryClient.prefetchQuery({ + queryKey: key2, + queryFn: () => 'data2', + }) + await queryClient.prefetchQuery({ + queryKey: [{ a: 'a', b: 'b' }], + queryFn: () => 'data3', + }) + await queryClient.prefetchQuery({ + queryKey: ['posts', 1], + queryFn: () => 'data4', + }) + queryClient.invalidateQueries({ queryKey: key2 }) + const query1 = queryCache.find({ queryKey: key1 })! + const query2 = queryCache.find({ queryKey: key2 })! + const query3 = queryCache.find({ queryKey: [{ a: 'a', b: 'b' }] })! + const query4 = queryCache.find({ queryKey: ['posts', 1] })! + + expect(queryCache.findAll({ queryKey: key1 })).toEqual([query1]) // wrapping in an extra array doesn't yield the same results anymore since v4 because keys need to be an array - expect(queryCache.findAll([key1])).toEqual([]) + expect(queryCache.findAll({ queryKey: [key1] })).toEqual([]) expect(queryCache.findAll()).toEqual([query1, query2, query3, query4]) expect(queryCache.findAll({})).toEqual([query1, query2, query3, query4]) - expect(queryCache.findAll(key1, { type: 'inactive' })).toEqual([query1]) - expect(queryCache.findAll(key1, { type: 'active' })).toEqual([]) - expect(queryCache.findAll(key1, { stale: true })).toEqual([]) - expect(queryCache.findAll(key1, { stale: false })).toEqual([query1]) + expect(queryCache.findAll({ queryKey: key1, type: 'inactive' })).toEqual([ + query1, + ]) + expect(queryCache.findAll({ queryKey: key1, type: 'active' })).toEqual([]) + expect(queryCache.findAll({ queryKey: key1, stale: true })).toEqual([]) + expect(queryCache.findAll({ queryKey: key1, stale: false })).toEqual([ + query1, + ]) expect( - queryCache.findAll(key1, { stale: false, type: 'active' }), + queryCache.findAll({ queryKey: key1, stale: false, type: 'active' }), ).toEqual([]) expect( - queryCache.findAll(key1, { stale: false, type: 'inactive' }), + queryCache.findAll({ queryKey: key1, stale: false, type: 'inactive' }), ).toEqual([query1]) expect( - queryCache.findAll(key1, { + queryCache.findAll({ + queryKey: key1, stale: false, type: 'inactive', exact: true, }), ).toEqual([query1]) - expect(queryCache.findAll(key2)).toEqual([query2]) - expect(queryCache.findAll(key2, { stale: undefined })).toEqual([query2]) - expect(queryCache.findAll(key2, { stale: true })).toEqual([query2]) - expect(queryCache.findAll(key2, { stale: false })).toEqual([]) - expect(queryCache.findAll([{ b: 'b' }])).toEqual([query3]) - expect(queryCache.findAll([{ a: 'a' }], { exact: false })).toEqual([ - query3, + expect(queryCache.findAll({ queryKey: key2 })).toEqual([query2]) + expect(queryCache.findAll({ queryKey: key2, stale: undefined })).toEqual([ + query2, ]) - expect(queryCache.findAll([{ a: 'a' }], { exact: true })).toEqual([]) - expect(queryCache.findAll([{ a: 'a', b: 'b' }], { exact: true })).toEqual( - [query3], - ) - expect(queryCache.findAll([{ a: 'a', b: 'b' }])).toEqual([query3]) - expect(queryCache.findAll([{ a: 'a', b: 'b', c: 'c' }])).toEqual([]) - expect(queryCache.findAll([{ a: 'a' }], { stale: false })).toEqual([ - query3, + expect(queryCache.findAll({ queryKey: key2, stale: true })).toEqual([ + query2, ]) - expect(queryCache.findAll([{ a: 'a' }], { stale: true })).toEqual([]) - expect(queryCache.findAll([{ a: 'a' }], { type: 'active' })).toEqual([]) - expect(queryCache.findAll([{ a: 'a' }], { type: 'inactive' })).toEqual([ + expect(queryCache.findAll({ queryKey: key2, stale: false })).toEqual([]) + expect(queryCache.findAll({ queryKey: [{ b: 'b' }] })).toEqual([query3]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], exact: false }), + ).toEqual([query3]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], exact: true }), + ).toEqual([]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a', b: 'b' }], exact: true }), + ).toEqual([query3]) + expect(queryCache.findAll({ queryKey: [{ a: 'a', b: 'b' }] })).toEqual([ query3, ]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a', b: 'b', c: 'c' }] }), + ).toEqual([]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], stale: false }), + ).toEqual([query3]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], stale: true }), + ).toEqual([]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], type: 'active' }), + ).toEqual([]) + expect( + queryCache.findAll({ queryKey: [{ a: 'a' }], type: 'inactive' }), + ).toEqual([query3]) expect( queryCache.findAll({ predicate: (query) => query === query3 }), ).toEqual([query3]) - expect(queryCache.findAll(['posts'])).toEqual([query4]) + expect(queryCache.findAll({ queryKey: ['posts'] })).toEqual([query4]) expect(queryCache.findAll({ fetchStatus: 'idle' })).toEqual([ query1, @@ -181,16 +214,19 @@ describe('queryCache', () => { query3, query4, ]) - expect(queryCache.findAll(key2, { fetchStatus: undefined })).toEqual([ - query2, - ]) - - const promise = queryClient.prefetchQuery(keyFetching, async () => { - await sleep(20) - return 'dataFetching' + expect( + queryCache.findAll({ queryKey: key2, fetchStatus: undefined }), + ).toEqual([query2]) + + const promise = queryClient.prefetchQuery({ + queryKey: keyFetching, + queryFn: async () => { + await sleep(20) + return 'dataFetching' + }, }) expect(queryCache.findAll({ fetchStatus: 'fetching' })).toEqual([ - queryCache.find(keyFetching), + queryCache.find({ queryKey: keyFetching }), ]) await promise expect(queryCache.findAll({ fetchStatus: 'fetching' })).toEqual([]) @@ -199,8 +235,14 @@ describe('queryCache', () => { test('should return all the queries when no filters are defined', async () => { const key1 = queryKey() const key2 = queryKey() - await queryClient.prefetchQuery(key1, () => 'data1') - await queryClient.prefetchQuery(key2, () => 'data2') + await queryClient.prefetchQuery({ + queryKey: key1, + queryFn: () => 'data1', + }) + await queryClient.prefetchQuery({ + queryKey: key2, + queryFn: () => 'data2', + }) expect(queryCache.findAll().length).toBe(2) }) }) @@ -211,10 +253,11 @@ describe('queryCache', () => { const onError = jest.fn() const testCache = new QueryCache({ onError }) const testClient = createQueryClient({ queryCache: testCache }) - await testClient.prefetchQuery(key, () => - Promise.reject('error'), - ) - const query = testCache.find(key) + await testClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.reject('error'), + }) + const query = testCache.find({ queryKey: key }) expect(onError).toHaveBeenCalledWith('error', query) }) }) @@ -225,8 +268,11 @@ describe('queryCache', () => { const onSuccess = jest.fn() const testCache = new QueryCache({ onSuccess }) const testClient = createQueryClient({ queryCache: testCache }) - await testClient.prefetchQuery(key, () => Promise.resolve({ data: 5 })) - const query = testCache.find(key) + await testClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve({ data: 5 }), + }) + const query = testCache.find({ queryKey: key }) expect(onSuccess).toHaveBeenCalledWith({ data: 5 }, query) }) }) @@ -236,7 +282,7 @@ describe('queryCache', () => { const key = queryKey() const hash = `["${key}"]` - await queryClient.prefetchQuery(key, () => 'data1') + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data1' }) // Directly add the query from the cache // to simulate a race condition @@ -256,7 +302,10 @@ describe('queryCache', () => { const key = queryKey() const hash = `["${key}"]` - await queryClient.prefetchQuery(key, () => 'data1') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data1', + }) // Directly remove the query from the cache // to simulate a race condition diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 8a91ef4632..051ea85c21 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -34,7 +34,7 @@ describe('queryClient', () => { defaultOptions: { queries: { queryFn } }, }) - expect(() => testClient.prefetchQuery(key)).not.toThrow() + expect(() => testClient.prefetchQuery({ queryKey: key })).not.toThrow() }) test('should merge defaultOptions when query is added to cache', async () => { @@ -47,8 +47,8 @@ describe('queryClient', () => { }) const fetchData = () => Promise.resolve('data') - await testClient.prefetchQuery(key, fetchData) - const newQuery = testClient.getQueryCache().find(key) + await testClient.prefetchQuery({ queryKey: key, queryFn: fetchData }) + const newQuery = testClient.getQueryCache().find({ queryKey: key }) expect(newQuery?.options.cacheTime).toBe(Infinity) }) @@ -91,7 +91,9 @@ describe('queryClient', () => { test('should not match if the query key is a subset', async () => { const key = queryKey() - queryClient.setQueryDefaults([key, 'a'], { queryFn: () => 'data' }) + queryClient.setQueryDefaults([key, 'a'], { + queryFn: () => 'data', + }) const observer = new QueryObserver(queryClient, { queryKey: [key], retry: false, @@ -118,8 +120,8 @@ describe('queryClient', () => { const key = queryKey() const queryOptions1 = { queryFn: () => 'data' } const queryOptions2 = { retry: false } - queryClient.setQueryDefaults(key, queryOptions1) - queryClient.setQueryDefaults(key, queryOptions2) + queryClient.setQueryDefaults(key, { ...queryOptions1 }) + queryClient.setQueryDefaults(key, { ...queryOptions2 }) expect(queryClient.getQueryDefaults(key)).toMatchObject(queryOptions2) }) @@ -271,7 +273,7 @@ describe('queryClient', () => { const testCache = testClient.getQueryCache() testClient.setQueryData(key, 'data') expect(testClient.getQueryData(key)).toBe('data') - expect(testCache.find(key)).toBe(testCache.get('someKey')) + expect(testCache.find({ queryKey: key })).toBe(testCache.get('someKey')) }) test('should create a new query if query was not found', () => { @@ -288,16 +290,24 @@ describe('queryClient', () => { test('should not create a new query if query was not found and data is undefined', () => { const key = queryKey() - expect(queryClient.getQueryCache().find(key)).toBe(undefined) + expect(queryClient.getQueryCache().find({ queryKey: key })).toBe( + undefined, + ) queryClient.setQueryData(key, undefined) - expect(queryClient.getQueryCache().find(key)).toBe(undefined) + expect(queryClient.getQueryCache().find({ queryKey: key })).toBe( + undefined, + ) }) test('should not create a new query if query was not found and updater returns undefined', () => { const key = queryKey() - expect(queryClient.getQueryCache().find(key)).toBe(undefined) + expect(queryClient.getQueryCache().find({ queryKey: key })).toBe( + undefined, + ) queryClient.setQueryData(key, () => undefined) - expect(queryClient.getQueryCache().find(key)).toBe(undefined) + expect(queryClient.getQueryCache().find({ queryKey: key })).toBe( + undefined, + ) }) test('should not update query data if data is undefined', () => { @@ -323,7 +333,9 @@ describe('queryClient', () => { queryClient.setQueryData(key, updater) expect(updater).toHaveBeenCalled() - expect(queryCache.find(key)!.state.data).toEqual('new data + test data') + expect(queryCache.find({ queryKey: key })!.state.data).toEqual( + 'new data + test data', + ) }) test('should use prev data if an isDataEqual function is defined and returns "true"', () => { @@ -335,7 +347,9 @@ describe('queryClient', () => { queryClient.setQueryData(key, 'prev data') queryClient.setQueryData(key, 'data') - expect(queryCache.find(key)!.state.data).toEqual('prev data') + expect(queryCache.find({ queryKey: key })!.state.data).toEqual( + 'prev data', + ) }) test('should set the new data without comparison if structuralSharing is set to false', () => { @@ -352,7 +366,7 @@ describe('queryClient', () => { queryClient.setQueryData(key, oldData) queryClient.setQueryData(key, newData) - expect(queryCache.find(key)!.state.data).toBe(newData) + expect(queryCache.find({ queryKey: key })!.state.data).toBe(newData) }) test('should apply a custom structuralSharing function when provided', () => { @@ -379,19 +393,22 @@ describe('queryClient', () => { queryClient.setQueryData(key, oldData) queryClient.setQueryData(key, newData) - expect(queryCache.find(key)!.state.data).toBe(oldData) + expect(queryCache.find({ queryKey: key })!.state.data).toBe(oldData) const distinctData = { value: new Date(2021, 11, 25) } queryClient.setQueryData(key, distinctData) - expect(queryCache.find(key)!.state.data).toBe(distinctData) + expect(queryCache.find({ queryKey: key })!.state.data).toBe(distinctData) }) test('should not set isFetching to false', async () => { const key = queryKey() - queryClient.prefetchQuery(key, async () => { - await sleep(10) - return 23 + queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 23 + }, }) expect(queryClient.getQueryState(key)).toMatchObject({ data: undefined, @@ -416,8 +433,9 @@ describe('queryClient', () => { queryClient.setQueryData(['key', 1], 1) queryClient.setQueryData(['key', 2], 2) - const result = queryClient.setQueriesData(['key'], (old) => - old ? old + 5 : undefined, + const result = queryClient.setQueriesData( + { queryKey: ['key'] }, + (old) => (old ? old + 5 : undefined), ) expect(result).toEqual([ @@ -431,7 +449,7 @@ describe('queryClient', () => { test('should accept queryFilters', () => { queryClient.setQueryData(['key', 1], 1) queryClient.setQueryData(['key', 2], 2) - const query1 = queryCache.find(['key', 1])! + const query1 = queryCache.find({ queryKey: ['key', 1] })! const result = queryClient.setQueriesData( { predicate: (query) => query === query1 }, @@ -444,7 +462,10 @@ describe('queryClient', () => { }) test('should not update non existing queries', () => { - const result = queryClient.setQueriesData(['key'], 'data') + const result = queryClient.setQueriesData( + { queryKey: ['key'] }, + 'data', + ) expect(result).toEqual([]) expect(queryClient.getQueryData(['key'])).toBe(undefined) @@ -499,7 +520,7 @@ describe('queryClient', () => { queryClient.setQueryData([key1, 1], 1) queryClient.setQueryData([key1, 2], 2) queryClient.setQueryData([key2, 2], 2) - expect(queryClient.getQueriesData([key1])).toEqual([ + expect(queryClient.getQueriesData({ queryKey: [key1] })).toEqual([ [[key1, 1], 1], [[key1, 2], 2], ]) @@ -507,13 +528,13 @@ describe('queryClient', () => { test('should return empty array if queries are not found', () => { const key = queryKey() - expect(queryClient.getQueriesData(key)).toEqual([]) + expect(queryClient.getQueriesData({ queryKey: key })).toEqual([]) }) test('should accept query filters', () => { queryClient.setQueryData(['key', 1], 1) queryClient.setQueryData(['key', 2], 2) - const query1 = queryCache.find(['key', 1])! + const query1 = queryCache.find({ queryKey: ['key', 1] })! const result = queryClient.getQueriesData({ predicate: (query) => query === query1, @@ -533,10 +554,10 @@ describe('queryClient', () => { Promise.resolve('data') await expect( - queryClient.fetchQuery( - key, - fetchFn, - ), + queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, + }), ).resolves.toEqual('data') }) @@ -545,8 +566,11 @@ describe('queryClient', () => { const key = queryKey() await expect( - queryClient.fetchQuery(key, async (): Promise => { - throw new Error('error') + queryClient.fetchQuery({ + queryKey: key, + queryFn: async (): Promise => { + throw new Error('error') + }, }), ).rejects.toEqual(new Error('error')) }) @@ -555,22 +579,28 @@ describe('queryClient', () => { const key = queryKey() const fetchFn = () => Promise.resolve('data') - const first = await queryClient.fetchQuery(key, fetchFn) - const second = await queryClient.fetchQuery(key, fetchFn) + const first = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, + }) + const second = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, + }) expect(second).toBe(first) }) test('should be able to fetch when cache time is set to 0 and then be removed', async () => { const key1 = queryKey() - const result = await queryClient.fetchQuery( - key1, - async () => { + const result = await queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 1 }, - { cacheTime: 0 }, - ) + cacheTime: 0, + }) expect(result).toEqual(1) await waitFor(() => expect(queryClient.getQueryData(key1)).toEqual(undefined), @@ -579,14 +609,14 @@ describe('queryClient', () => { test('should keep a query in cache if cache time is Infinity', async () => { const key1 = queryKey() - const result = await queryClient.fetchQuery( - key1, - async () => { + const result = await queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 1 }, - { cacheTime: Infinity }, - ) + cacheTime: Infinity, + }) const result2 = queryClient.getQueryData(key1) expect(result).toEqual(1) expect(result2).toEqual(1) @@ -597,7 +627,9 @@ describe('queryClient', () => { queryClient.setQueryData(key, 'og') const fetchFn = () => Promise.resolve('new') - const first = await queryClient.fetchQuery(key, fetchFn, { + const first = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, initialData: 'initial', staleTime: 100, }) @@ -611,18 +643,26 @@ describe('queryClient', () => { const fetchFn = () => ++count queryClient.setQueryData(key, count) - const first = await queryClient.fetchQuery(key, fetchFn, { + const first = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, staleTime: 100, }) await sleep(11) - const second = await queryClient.fetchQuery(key, fetchFn, { + const second = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, staleTime: 10, }) - const third = await queryClient.fetchQuery(key, fetchFn, { + const third = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, staleTime: 10, }) await sleep(11) - const fourth = await queryClient.fetchQuery(key, fetchFn, { + const fourth = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, staleTime: 10, }) expect(first).toBe(0) @@ -652,16 +692,16 @@ describe('queryClient', () => { any, StrictData, StrictQueryKey - >(key, fetchFn), + >({ queryKey: key, queryFn: fetchFn }), ).resolves.toEqual(data) }) test('should return infinite query data', async () => { const key = queryKey() - const result = await queryClient.fetchInfiniteQuery( - key, - ({ pageParam = 10 }) => Number(pageParam), - ) + const result = await queryClient.fetchInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }) => Number(pageParam), + }) const result2 = queryClient.getQueryData(key) const expected = { @@ -688,7 +728,7 @@ describe('queryClient', () => { any, StrictData, StrictQueryKey - >(key, fetchFn) + >({ queryKey: key, queryFn: fetchFn }) const result = queryClient.getQueryData(key) @@ -701,9 +741,10 @@ describe('queryClient', () => { test('should return infinite query data', async () => { const key = queryKey() - await queryClient.prefetchInfiniteQuery(key, ({ pageParam = 10 }) => - Number(pageParam), - ) + await queryClient.prefetchInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }) => Number(pageParam), + }) const result = queryClient.getQueryData(key) @@ -728,7 +769,7 @@ describe('queryClient', () => { any, StrictData, StrictQueryKey - >(key, fetchFn) + >({ queryKey: key, queryFn: fetchFn }) const result = queryClient.getQueryData(key) @@ -738,15 +779,13 @@ describe('queryClient', () => { test('should return undefined when an error is thrown', async () => { const key = queryKey() - const result = await queryClient.prefetchQuery( - key, - async (): Promise => { + const result = await queryClient.prefetchQuery({ + queryKey: key, + queryFn: async (): Promise => { throw new Error('error') }, - { - retry: false, - }, - ) + retry: false, + }) expect(result).toBeUndefined() expect(mockLogger.error).toHaveBeenCalled() @@ -755,16 +794,16 @@ describe('queryClient', () => { test('should be garbage collected after cacheTime if unused', async () => { const key = queryKey() - await queryClient.prefetchQuery( - key, - async () => { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => { return 'data' }, - { cacheTime: 10 }, - ) - expect(queryCache.find(key)).toBeDefined() + cacheTime: 10, + }) + expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(15) - expect(queryCache.find(key)).not.toBeDefined() + expect(queryCache.find({ queryKey: key })).not.toBeDefined() }) }) @@ -775,8 +814,8 @@ describe('queryClient', () => { const fetchFn = () => Promise.resolve('data') // check the query was added to the cache - await queryClient.prefetchQuery(key, fetchFn) - expect(queryCache.find(key)).toBeTruthy() + await queryClient.prefetchQuery({ queryKey: key, queryFn: fetchFn }) + expect(queryCache.find({ queryKey: key })).toBeTruthy() // check the error doesn't occur expect(() => @@ -784,7 +823,7 @@ describe('queryClient', () => { ).not.toThrow() // check query was successful removed - expect(queryCache.find(key)).toBeFalsy() + expect(queryCache.find({ queryKey: key })).toBeFalsy() }) }) @@ -793,27 +832,42 @@ describe('queryClient', () => { const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() - await queryClient.fetchQuery(key1, async () => { - return 'data' + await queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { + return 'data' + }, }) try { - await queryClient.fetchQuery(key2, async () => { - return Promise.reject('err') + await queryClient.fetchQuery({ + queryKey: key2, + queryFn: async () => { + return Promise.reject('err') + }, }) } catch {} - queryClient.fetchQuery(key1, async () => { - await sleep(1000) - return 'data2' + queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(1000) + return 'data2' + }, }) try { - queryClient.fetchQuery(key2, async () => { - await sleep(1000) - return Promise.reject('err2') + queryClient.fetchQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(1000) + return Promise.reject('err2') + }, }) } catch {} - queryClient.fetchQuery(key3, async () => { - await sleep(1000) - return 'data3' + queryClient.fetchQuery({ + queryKey: key3, + queryFn: async () => { + await sleep(1000) + return 'data3' + }, }) await sleep(10) await queryClient.cancelQueries() @@ -838,15 +892,21 @@ describe('queryClient', () => { test('should not revert if revert option is set to false', async () => { const key1 = queryKey() - await queryClient.fetchQuery(key1, async () => { - return 'data' + await queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { + return 'data' + }, }) - queryClient.fetchQuery(key1, async () => { - await sleep(1000) - return 'data2' + queryClient.fetchQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(1000) + return 'data2' + }, }) await sleep(10) - await queryClient.cancelQueries(key1, {}, { revert: false }) + await queryClient.cancelQueries({ queryKey: key1 }, { revert: false }) const state1 = queryClient.getQueryState(key1) expect(state1).toMatchObject({ status: 'error', @@ -858,7 +918,7 @@ describe('queryClient', () => { test('should not refetch if all observers are disabled', async () => { const key = queryKey() const queryFn = jest.fn().mockReturnValue('data') - await queryClient.fetchQuery(key, queryFn) + await queryClient.fetchQuery({ queryKey: key, queryFn }) const observer1 = new QueryObserver(queryClient, { queryKey: key, queryFn, @@ -872,7 +932,7 @@ describe('queryClient', () => { test('should refetch if at least one observer is enabled', async () => { const key = queryKey() const queryFn = jest.fn().mockReturnValue('data') - await queryClient.fetchQuery(key, queryFn) + await queryClient.fetchQuery({ queryKey: key, queryFn }) const observer1 = new QueryObserver(queryClient, { queryKey: key, queryFn, @@ -895,8 +955,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer1 = new QueryObserver(queryClient, { queryKey: key1, staleTime: Infinity, @@ -921,8 +981,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -940,14 +1000,14 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, }) const unsubscribe = observer.subscribe(() => undefined) - queryClient.invalidateQueries(key1) + queryClient.invalidateQueries({ queryKey: key1 }) await queryClient.refetchQueries({ stale: true }) unsubscribe() // fetchQuery, observer mount, invalidation (cancels observer mount) and refetch @@ -960,9 +1020,9 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) - queryClient.invalidateQueries(key1) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) + queryClient.invalidateQueries({ queryKey: key1 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -982,8 +1042,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1001,8 +1061,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1020,8 +1080,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1039,8 +1099,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1082,15 +1142,15 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, staleTime: Infinity, }) const unsubscribe = observer.subscribe(() => undefined) - queryClient.invalidateQueries(key1) + queryClient.invalidateQueries({ queryKey: key1 }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) @@ -1101,15 +1161,15 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, enabled: false, staleTime: Infinity, }) const unsubscribe = observer.subscribe(() => undefined) - queryClient.invalidateQueries(key1) + queryClient.invalidateQueries({ queryKey: key1 }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) expect(queryFn2).toHaveBeenCalledTimes(1) @@ -1120,15 +1180,16 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, staleTime: Infinity, }) const unsubscribe = observer.subscribe(() => undefined) - queryClient.invalidateQueries(key1, { + queryClient.invalidateQueries({ + queryKey: key1, refetchType: 'none', }) unsubscribe() @@ -1141,8 +1202,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1152,7 +1213,8 @@ describe('queryClient', () => { const unsubscribe = observer.subscribe(() => undefined) unsubscribe() - await queryClient.invalidateQueries(key1, { + await queryClient.invalidateQueries({ + queryKey: key1, refetchType: 'inactive', }) expect(queryFn1).toHaveBeenCalledTimes(2) @@ -1164,8 +1226,8 @@ describe('queryClient', () => { const key2 = queryKey() const queryFn1 = jest.fn().mockReturnValue('data1') const queryFn2 = jest.fn().mockReturnValue('data2') - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) + await queryClient.fetchQuery({ queryKey: key1, queryFn: queryFn1 }) + await queryClient.fetchQuery({ queryKey: key2, queryFn: queryFn2 }) const observer = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, @@ -1237,11 +1299,11 @@ describe('queryClient', () => { const callback = jest.fn() - await queryClient.prefetchQuery(key, () => 'data') + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) queryCache.subscribe(callback) - queryClient.resetQueries(key) + queryClient.resetQueries({ queryKey: key }) expect(callback).toHaveBeenCalled() }) @@ -1249,13 +1311,13 @@ describe('queryClient', () => { test('should reset query', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data') + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) let state = queryClient.getQueryState(key) expect(state?.data).toEqual('data') expect(state?.status).toEqual('success') - queryClient.resetQueries(key) + queryClient.resetQueries({ queryKey: key }) state = queryClient.getQueryState(key) @@ -1268,14 +1330,16 @@ describe('queryClient', () => { test('should reset query data to initial data if set', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'data', { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'data', initialData: 'initial', }) let state = queryClient.getQueryState(key) expect(state?.data).toEqual('data') - queryClient.resetQueries(key) + queryClient.resetQueries({ queryKey: key }) state = queryClient.getQueryState(key) diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index 89e9841664..a87e76cf0b 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -383,7 +383,7 @@ describe('queryObserver', () => { enabled: false, }) const unsubscribe = observer.subscribe(callback) - await queryClient.fetchQuery(key, queryFn) + await queryClient.fetchQuery({ queryKey: key, queryFn }) unsubscribe() expect(queryFn).toHaveBeenCalledTimes(1) expect(callback).toHaveBeenCalledTimes(2) @@ -401,7 +401,7 @@ describe('queryObserver', () => { results.push(x) }) observer.setOptions({ enabled: false, staleTime: 10 }) - await queryClient.fetchQuery(key, queryFn) + await queryClient.fetchQuery({ queryKey: key, queryFn }) await sleep(100) unsubscribe() expect(queryFn).toHaveBeenCalledTimes(1) @@ -426,7 +426,7 @@ describe('queryObserver', () => { const unsubscribe2 = observer.subscribe((x) => { results2.push(x) }) - await queryClient.fetchQuery(key, queryFn) + await queryClient.fetchQuery({ queryKey: key, queryFn }) await sleep(50) unsubscribe1() unsubscribe2() @@ -475,7 +475,7 @@ describe('queryObserver', () => { // @ts-expect-error expect(observer.refetchIntervalId).toBeUndefined() await sleep(10) - expect(queryClient.getQueryCache().find(key)).toBeUndefined() + expect(queryClient.getQueryCache().find({ queryKey: key })).toBeUndefined() }) test('uses placeholderData as non-cache data when loading a query with no data', async () => { @@ -772,11 +772,11 @@ describe('queryObserver', () => { const data1 = { value: 'data 1' } const data2 = { value: 'data 2' } - await queryClient.prefetchQuery(key, () => data1) + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => data1 }) const observer = new QueryObserver(queryClient, { queryKey: key, }) - await queryClient.prefetchQuery(key, () => data2) + await queryClient.prefetchQuery({ queryKey: key, queryFn: () => data2 }) const spy = jest.fn() const unsubscribe = queryClient.getQueryCache().subscribe(spy) @@ -791,7 +791,7 @@ describe('queryObserver', () => { const key = queryKey() const queryFn = () => 'data' - await queryClient.prefetchQuery(key, queryFn) + await queryClient.prefetchQuery({ queryKey: key, queryFn }) const observer = new QueryObserver(queryClient, { queryKey: key, queryFn, @@ -800,7 +800,7 @@ describe('queryObserver', () => { const spy = jest.fn() const unsubscribe = observer.subscribe(spy) - await queryClient.refetchQueries(key) + await queryClient.refetchQueries({ queryKey: key }) await sleep(10) // Force isStale to true diff --git a/packages/query-core/src/tests/utils.test.tsx b/packages/query-core/src/tests/utils.test.tsx index 415dc0f1c6..8deefe47b5 100644 --- a/packages/query-core/src/tests/utils.test.tsx +++ b/packages/query-core/src/tests/utils.test.tsx @@ -2,7 +2,6 @@ import { replaceEqualDeep, partialDeepEqual, isPlainObject, - parseMutationArgs, matchMutation, scheduleMicrotask, sleep, @@ -327,13 +326,6 @@ describe('core/utils', () => { }) }) - describe('parseMutationArgs', () => { - it('should return mutation options', () => { - const options = { mutationKey: ['key'] } - expect(parseMutationArgs(options)).toMatchObject(options) - }) - }) - describe('matchMutation', () => { it('should return false if mutationKey options is undefined', () => { const filters = { mutationKey: ['key1'] } diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 2ce6df9f64..720ea15f5b 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -294,7 +294,10 @@ export interface FetchQueryOptions< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends QueryOptions { +> extends WithRequired< + QueryOptions, + 'queryKey' + > { /** * The time in milliseconds after data is considered stale. * If the data is fresh it will be returned from the cache. diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 5adf3c8fde..d24543ffed 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -1,14 +1,6 @@ import type { Mutation } from './mutation' import type { Query } from './query' -import type { - FetchStatus, - MutationFunction, - MutationKey, - MutationOptions, - QueryFunction, - QueryKey, - QueryOptions, -} from './types' +import type { FetchStatus, MutationKey, QueryKey, QueryOptions } from './types' // TYPES @@ -101,74 +93,6 @@ export function timeUntilStale(updatedAt: number, staleTime?: number): number { return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0) } -export function parseQueryArgs< - TOptions extends QueryOptions, - TQueryKey extends QueryKey = QueryKey, ->( - arg1: TQueryKey | TOptions, - arg2?: QueryFunction | TOptions, - arg3?: TOptions, -): TOptions { - if (!isQueryKey(arg1)) { - return arg1 as TOptions - } - - if (typeof arg2 === 'function') { - return { ...arg3, queryKey: arg1, queryFn: arg2 } as TOptions - } - - return { ...arg2, queryKey: arg1 } as TOptions -} - -export function parseMutationArgs< - TOptions extends MutationOptions, ->( - arg1: MutationKey | MutationFunction | TOptions, - arg2?: MutationFunction | TOptions, - arg3?: TOptions, -): TOptions { - if (isQueryKey(arg1)) { - if (typeof arg2 === 'function') { - return { ...arg3, mutationKey: arg1, mutationFn: arg2 } as TOptions - } - return { ...arg2, mutationKey: arg1 } as TOptions - } - - if (typeof arg1 === 'function') { - return { ...arg2, mutationFn: arg1 } as TOptions - } - - return { ...arg1 } as TOptions -} - -export function parseFilterArgs< - TFilters extends QueryFilters, - TOptions = unknown, ->( - arg1?: QueryKey | TFilters, - arg2?: TFilters | TOptions, - arg3?: TOptions, -): [TFilters, TOptions | undefined] { - return ( - isQueryKey(arg1) ? [{ ...arg2, queryKey: arg1 }, arg3] : [arg1 || {}, arg2] - ) as [TFilters, TOptions] -} - -export function parseMutationFilterArgs< - TFilters extends MutationFilters, - TOptions = unknown, ->( - arg1?: QueryKey | TFilters, - arg2?: TFilters | TOptions, - arg3?: TOptions, -): [TFilters, TOptions | undefined] { - return ( - isQueryKey(arg1) - ? [{ ...arg2, mutationKey: arg1 }, arg3] - : [arg1 || {}, arg2] - ) as [TFilters, TOptions] -} - export function matchQuery( filters: QueryFilters, query: Query, diff --git a/packages/query-sync-storage-persister/src/__tests__/storageIsFull.test.ts b/packages/query-sync-storage-persister/src/__tests__/storageIsFull.test.ts index 0eb795c3ce..1c787ac2e2 100644 --- a/packages/query-sync-storage-persister/src/__tests__/storageIsFull.test.ts +++ b/packages/query-sync-storage-persister/src/__tests__/storageIsFull.test.ts @@ -51,13 +51,26 @@ describe('createpersister ', () => { storage, }) - await queryClient.prefetchQuery(['string'], () => Promise.resolve('string')) - await queryClient.prefetchQuery(['number'], () => Promise.resolve(1)) - await queryClient.prefetchQuery(['boolean'], () => Promise.resolve(true)) - await queryClient.prefetchQuery(['null'], () => Promise.resolve(null)) - await queryClient.prefetchQuery(['array'], () => - Promise.resolve(['string', 0]), - ) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => Promise.resolve('string'), + }) + await queryClient.prefetchQuery({ + queryKey: ['number'], + queryFn: () => Promise.resolve(1), + }) + await queryClient.prefetchQuery({ + queryKey: ['boolean'], + queryFn: () => Promise.resolve(true), + }) + await queryClient.prefetchQuery({ + queryKey: ['null'], + queryFn: () => Promise.resolve(null), + }) + await queryClient.prefetchQuery({ + queryKey: ['array'], + queryFn: () => Promise.resolve(['string', 0]), + }) const persistClient = { buster: 'test-buster', @@ -83,16 +96,31 @@ describe('createpersister ', () => { retry: removeOldestQuery, }) - await queryClient.prefetchQuery(['A'], () => Promise.resolve('A'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['A'], + queryFn: () => Promise.resolve('A'.repeat(N)), + }) await sleep(1) - await queryClient.prefetchQuery(['B'], () => Promise.resolve('B'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['B'], + queryFn: () => Promise.resolve('B'.repeat(N)), + }) await sleep(1) - await queryClient.prefetchQuery(['C'], () => Promise.resolve('C'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['C'], + queryFn: () => Promise.resolve('C'.repeat(N)), + }) await sleep(1) - await queryClient.prefetchQuery(['D'], () => Promise.resolve('D'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['D'], + queryFn: () => Promise.resolve('D'.repeat(N)), + }) await sleep(1) - await queryClient.prefetchQuery(['E'], () => Promise.resolve('E'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['E'], + queryFn: () => Promise.resolve('E'.repeat(N)), + }) const persistClient = { buster: 'test-limit', @@ -111,7 +139,10 @@ describe('createpersister ', () => { ).not.toBeUndefined() // update query Data - await queryClient.prefetchQuery(['A'], () => Promise.resolve('a'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['A'], + queryFn: () => Promise.resolve('a'.repeat(N)), + }) const updatedPersistClient = { buster: 'test-limit', timestamp: Date.now(), @@ -141,7 +172,10 @@ describe('createpersister ', () => { retry: removeOldestQuery, }) - await queryClient.prefetchQuery(['A'], () => Promise.resolve('A'.repeat(N))) + await queryClient.prefetchQuery({ + queryKey: ['A'], + queryFn: () => Promise.resolve('A'.repeat(N)), + }) await sleep(1) const persistClient = { diff --git a/packages/react-query-devtools/src/__tests__/devtools.test.tsx b/packages/react-query-devtools/src/__tests__/devtools.test.tsx index 61d00c6b45..323a441fd6 100644 --- a/packages/react-query-devtools/src/__tests__/devtools.test.tsx +++ b/packages/react-query-devtools/src/__tests__/devtools.test.tsx @@ -44,9 +44,12 @@ describe('ReactQueryDevtools', () => { const onToggleClick = jest.fn() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -118,9 +121,12 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -151,14 +157,14 @@ describe('ReactQueryDevtools', () => { const { queryClient, queryCache } = createQueryClient() function Page() { - const { data = 'default' } = useQuery( - ['check'], - async () => { + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { await sleep(100) return 'test' }, - { staleTime: 300 }, - ) + staleTime: 300, + }) return (
@@ -193,7 +199,7 @@ describe('ReactQueryDevtools', () => { screen.getByRole('button', { name: /open react query devtools/i }), ) - const currentQuery = queryCache.find(['check']) + const currentQuery = queryCache.find({ queryKey: ['check'] }) // When the query is fetching then expect number of // fetching queries to be 1 @@ -243,9 +249,12 @@ describe('ReactQueryDevtools', () => { const { queryClient, queryCache } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -261,7 +270,7 @@ describe('ReactQueryDevtools', () => { screen.getByRole('button', { name: /open react query devtools/i }), ) - const currentQuery = queryCache.find(['check']) + const currentQuery = queryCache.find({ queryKey: ['check'] }) await screen.findByText(getByTextContent(`1${currentQuery?.queryHash}`)) @@ -278,19 +287,28 @@ describe('ReactQueryDevtools', () => { const { queryClient, queryCache } = createQueryClient() function Page() { - const fooResult = useQuery(['foo'], async () => { - await sleep(10) - return 'foo-result' + const fooResult = useQuery({ + queryKey: ['foo'], + queryFn: async () => { + await sleep(10) + return 'foo-result' + }, }) - const barResult = useQuery(['bar'], async () => { - await sleep(10) - return 'bar-result' + const barResult = useQuery({ + queryKey: ['bar'], + queryFn: async () => { + await sleep(10) + return 'bar-result' + }, }) - const bazResult = useQuery(['baz'], async () => { - await sleep(10) - return 'baz-result' + const bazResult = useQuery({ + queryKey: ['baz'], + queryFn: async () => { + await sleep(10) + return 'baz-result' + }, }) return ( @@ -308,9 +326,12 @@ describe('ReactQueryDevtools', () => { screen.getByRole('button', { name: /open react query devtools/i }), ) - const fooQueryHash = queryCache.find(['foo'])?.queryHash ?? 'invalid hash' - const barQueryHash = queryCache.find(['bar'])?.queryHash ?? 'invalid hash' - const bazQueryHash = queryCache.find(['baz'])?.queryHash ?? 'invalid hash' + const fooQueryHash = + queryCache.find({ queryKey: ['foo'] })?.queryHash ?? 'invalid hash' + const barQueryHash = + queryCache.find({ queryKey: ['bar'] })?.queryHash ?? 'invalid hash' + const bazQueryHash = + queryCache.find({ queryKey: ['baz'] })?.queryHash ?? 'invalid hash' await screen.findByText(fooQueryHash) screen.getByText(barQueryHash) @@ -333,16 +354,14 @@ describe('ReactQueryDevtools', () => { function Page() { const [enabled, setEnabled] = React.useState(false) - const { data } = useQuery( - ['key'], - async () => { + const { data } = useQuery({ + queryKey: ['key'], + queryFn: async () => { await sleep(10) return 'test' }, - { - enabled, - }, - ) + enabled, + }) return (
@@ -367,7 +386,9 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data } = useQuery(['key'], () => Promise.resolve('test'), { + const { data } = useQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('test'), enabled: false, }) @@ -405,9 +426,12 @@ describe('ReactQueryDevtools', () => { let count = 0 function App() { - const { data, fetchStatus } = useQuery(['key'], () => { - count++ - return Promise.resolve('test') + const { data, fetchStatus } = useQuery({ + queryKey: ['key'], + queryFn: () => { + count++ + return Promise.resolve('test') + }, }) return ( @@ -458,30 +482,33 @@ describe('ReactQueryDevtools', () => { const { queryClient, queryCache } = createQueryClient() function Page() { - const query1Result = useQuery(['query-1'], async () => { - await sleep(20) - return 'query-1-result' + const query1Result = useQuery({ + queryKey: ['query-1'], + queryFn: async () => { + await sleep(20) + return 'query-1-result' + }, }) - const query3Result = useQuery( - ['query-3'], - async () => { + const query3Result = useQuery({ + queryKey: ['query-3'], + queryFn: async () => { await sleep(10) return 'query-3-result' }, - { staleTime: Infinity, enabled: typeof query1Result.data === 'string' }, - ) + staleTime: Infinity, + enabled: typeof query1Result.data === 'string', + }) - const query2Result = useQuery( - ['query-2'], - async () => { + const query2Result = useQuery({ + queryKey: ['query-2'], + queryFn: async () => { await sleep(10) return 'query-2-result' }, - { - enabled: typeof query3Result.data === 'string', - }, - ) + + enabled: typeof query3Result.data === 'string', + }) return (
@@ -498,9 +525,12 @@ describe('ReactQueryDevtools', () => { screen.getByRole('button', { name: /open react query devtools/i }), ) - const query1Hash = queryCache.find(['query-1'])?.queryHash ?? 'invalid hash' - const query2Hash = queryCache.find(['query-2'])?.queryHash ?? 'invalid hash' - const query3Hash = queryCache.find(['query-3'])?.queryHash ?? 'invalid hash' + const query1Hash = + queryCache.find({ queryKey: ['query-1'] })?.queryHash ?? 'invalid hash' + const query2Hash = + queryCache.find({ queryKey: ['query-2'] })?.queryHash ?? 'invalid hash' + const query3Hash = + queryCache.find({ queryKey: ['query-3'] })?.queryHash ?? 'invalid hash' const sortSelect = screen.getByLabelText(/sort queries/i) let queries = [] @@ -563,9 +593,12 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -603,9 +636,12 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -632,9 +668,12 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -676,7 +715,9 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test', { + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', context, }) @@ -703,7 +744,9 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test', { + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', throwErrors: true, }) @@ -738,7 +781,9 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test', { + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', throwErrors: true, context, }) @@ -770,7 +815,10 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test') + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', + }) return (
@@ -794,7 +842,10 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test') + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', + }) return (
@@ -827,7 +878,10 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test') + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', + }) return (
@@ -869,7 +923,10 @@ describe('ReactQueryDevtools', () => { const { queryClient } = createQueryClient() function Page() { - const { data = 'default' } = useQuery(['check'], async () => 'test') + const { data = 'default' } = useQuery({ + queryKey: ['check'], + queryFn: async () => 'test', + }) return (
diff --git a/packages/react-query-devtools/src/devtools.tsx b/packages/react-query-devtools/src/devtools.tsx index 9de970493c..695fa3dab3 100644 --- a/packages/react-query-devtools/src/devtools.tsx +++ b/packages/react-query-devtools/src/devtools.tsx @@ -1109,27 +1109,27 @@ const QueryRow = React.memo( const queryHash = useSubscribeToQueryCache( queryCache, - () => queryCache.find(queryKey)?.queryHash, + () => queryCache.find({ queryKey })?.queryHash, ) ?? '' const queryState = useSubscribeToQueryCache( queryCache, - () => queryCache.find(queryKey)?.state, + () => queryCache.find({ queryKey })?.state, ) const isStale = useSubscribeToQueryCache(queryCache, () => - queryCache.find(queryKey)?.isStale(), + queryCache.find({ queryKey })?.isStale(), ) ?? false const isDisabled = useSubscribeToQueryCache(queryCache, () => - queryCache.find(queryKey)?.isDisabled(), + queryCache.find({ queryKey })?.isDisabled(), ) ?? false const observerCount = useSubscribeToQueryCache(queryCache, () => - queryCache.find(queryKey)?.getObserversCount(), + queryCache.find({ queryKey })?.getObserversCount(), ) ?? 0 if (!queryState) { diff --git a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx index 5d0e93f2c5..7522b8872a 100644 --- a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx +++ b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx @@ -57,7 +57,10 @@ describe('PersistQueryClientProvider', () => { const states: UseQueryResult[] = [] const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -66,9 +69,12 @@ describe('PersistQueryClientProvider', () => { queryClient.clear() function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'fetched' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, }) states.push(state) @@ -126,7 +132,10 @@ describe('PersistQueryClientProvider', () => { const states: UseQueryResult[] = [] const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -202,7 +211,10 @@ describe('PersistQueryClientProvider', () => { const states: DefinedUseQueryResult[] = [] const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -211,19 +223,18 @@ describe('PersistQueryClientProvider', () => { queryClient.clear() function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'fetched' }, - { - initialData: 'initial', - // make sure that initial data is older than the hydration data - // otherwise initialData would be newer and takes precedence - initialDataUpdatedAt: 1, - }, - ) + + initialData: 'initial', + // make sure that initial data is older than the hydration data + // otherwise initialData would be newer and takes precedence + initialDataUpdatedAt: 1, + }) states.push(state) @@ -280,7 +291,10 @@ describe('PersistQueryClientProvider', () => { const states: UseQueryResult[] = [] const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -289,16 +303,15 @@ describe('PersistQueryClientProvider', () => { queryClient.clear() function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'fetched' }, - { - staleTime: Infinity, - }, - ) + + staleTime: Infinity, + }) states.push(state) @@ -341,7 +354,10 @@ describe('PersistQueryClientProvider', () => { const key = queryKey() const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -350,9 +366,12 @@ describe('PersistQueryClientProvider', () => { queryClient.clear() function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'fetched' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, }) return ( @@ -392,9 +411,12 @@ describe('PersistQueryClientProvider', () => { const [error, persister] = createMockErrorPersister(removeClient) function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'fetched' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, }) return ( @@ -425,7 +447,10 @@ describe('PersistQueryClientProvider', () => { const states: UseQueryResult[] = [] const queryClient = createQueryClient() - await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated')) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) const persister = createMockPersister() @@ -480,7 +505,7 @@ describe('PersistQueryClientProvider', () => { } function Page() { - const state = useQuery(key) + const state = useQuery({ queryKey: key }) states.push(state) diff --git a/packages/react-query/src/__tests__/Hydrate.test.tsx b/packages/react-query/src/__tests__/Hydrate.test.tsx index ab8939574f..c0219fc518 100644 --- a/packages/react-query/src/__tests__/Hydrate.test.tsx +++ b/packages/react-query/src/__tests__/Hydrate.test.tsx @@ -23,9 +23,10 @@ describe('React hydration', () => { beforeAll(async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => - dataQuery(['stringCached']), - ) + await queryClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['stringCached']), + }) const dehydrated = dehydrate(queryClient) stringifiedState = JSON.stringify(dehydrated) queryClient.clear() @@ -39,7 +40,10 @@ describe('React hydration', () => { function Page() { useHydrate(dehydratedState) - const { data } = useQuery(['string'], () => dataQuery(['string'])) + const { data } = useQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['string']), + }) return (

{data}

@@ -71,7 +75,9 @@ describe('React hydration', () => { function Page() { useHydrate(dehydratedState, { context }) - const { data } = useQuery(['string'], () => dataQuery(['string']), { + const { data } = useQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['string']), context, }) return ( @@ -104,7 +110,10 @@ describe('React hydration', () => { const queryClient = createQueryClient({ queryCache }) function Page({ queryKey }: { queryKey: [string] }) { - const { data } = useQuery(queryKey, () => dataQuery(queryKey)) + const { data } = useQuery({ + queryKey, + queryFn: () => dataQuery(queryKey), + }) return (

{data}

@@ -126,12 +135,14 @@ describe('React hydration', () => { const intermediateClient = createQueryClient({ queryCache: intermediateCache, }) - await intermediateClient.prefetchQuery(['string'], () => - dataQuery(['should change']), - ) - await intermediateClient.prefetchQuery(['added string'], () => - dataQuery(['added string']), - ) + await intermediateClient.prefetchQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['should change']), + }) + await intermediateClient.prefetchQuery({ + queryKey: ['added string'], + queryFn: () => dataQuery(['added string']), + }) const dehydrated = dehydrate(intermediateClient) intermediateClient.clear() @@ -160,7 +171,10 @@ describe('React hydration', () => { const queryClient = createQueryClient({ queryCache }) function Page() { - const { data } = useQuery(['string'], () => dataQuery(['string'])) + const { data } = useQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['string']), + }) return (

{data}

diff --git a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx index 32c1d67020..1c67c74540 100644 --- a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx @@ -19,9 +19,12 @@ describe('QueryClientProvider', () => { const queryClient = createQueryClient({ queryCache }) function Page() { - const { data } = useQuery(key, async () => { - await sleep(10) - return 'test' + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -39,7 +42,7 @@ describe('QueryClientProvider', () => { await waitFor(() => rendered.getByText('test')) - expect(queryCache.find(key)).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() }) test('allows multiple caches to be partitioned', async () => { @@ -53,9 +56,12 @@ describe('QueryClientProvider', () => { const queryClient2 = createQueryClient({ queryCache: queryCache2 }) function Page1() { - const { data } = useQuery(key1, async () => { - await sleep(10) - return 'test1' + const { data } = useQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 'test1' + }, }) return ( @@ -65,9 +71,12 @@ describe('QueryClientProvider', () => { ) } function Page2() { - const { data } = useQuery(key2, async () => { - await sleep(10) - return 'test2' + const { data } = useQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 'test2' + }, }) return ( @@ -91,10 +100,10 @@ describe('QueryClientProvider', () => { await waitFor(() => rendered.getByText('test1')) await waitFor(() => rendered.getByText('test2')) - expect(queryCache1.find(key1)).toBeDefined() - expect(queryCache1.find(key2)).not.toBeDefined() - expect(queryCache2.find(key1)).not.toBeDefined() - expect(queryCache2.find(key2)).toBeDefined() + expect(queryCache1.find({ queryKey: key1 })).toBeDefined() + expect(queryCache1.find({ queryKey: key2 })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key1 })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key2 })).toBeDefined() }) test("uses defaultOptions for queries when they don't provide their own config", async () => { @@ -111,9 +120,12 @@ describe('QueryClientProvider', () => { }) function Page() { - const { data } = useQuery(key, async () => { - await sleep(10) - return 'test' + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -131,8 +143,8 @@ describe('QueryClientProvider', () => { await waitFor(() => rendered.getByText('test')) - expect(queryCache.find(key)).toBeDefined() - expect(queryCache.find(key)?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key })).toBeDefined() + expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) }) describe('with custom context', () => { @@ -158,16 +170,20 @@ describe('QueryClientProvider', () => { }) function Page() { - const { data: testOuter } = useQuery(key, async () => 'testOuter', { + const { data: testOuter } = useQuery({ + queryKey: key, + queryFn: async () => 'testOuter', context: contextOuter, }) - const { data: testInner } = useQuery(key, async () => 'testInner', { + const { data: testInner } = useQuery({ + queryKey: key, + queryFn: async () => 'testInner', context: contextInner, }) - const { data: testInnerInner } = useQuery( - key, - async () => 'testInnerInner', - ) + const { data: testInnerInner } = useQuery({ + queryKey: key, + queryFn: async () => 'testInnerInner', + }) return (
diff --git a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx index 49fc6d5fad..12633a0ab1 100644 --- a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx +++ b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx @@ -22,9 +22,9 @@ describe('QueryErrorResetBoundary', () => { let succeed = false function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -32,11 +32,9 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - throwErrors: true, - }, - ) + retry: false, + throwErrors: true, + }) return
{data}
} @@ -78,9 +76,9 @@ describe('QueryErrorResetBoundary', () => { let succeed = false function Page() { - const { data, status } = useQuery( - key, - async () => { + const { data, status } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -88,12 +86,10 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - enabled: !succeed, - throwErrors: true, - }, - ) + retry: false, + enabled: !succeed, + throwErrors: true, + }) return (
status: {status}
@@ -141,9 +137,9 @@ describe('QueryErrorResetBoundary', () => { function Page() { const [enabled, setEnabled] = React.useState(false) - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -151,12 +147,10 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - enabled, - throwErrors: true, - }, - ) + retry: false, + enabled, + throwErrors: true, + }) React.useEffect(() => { setEnabled(true) @@ -201,17 +195,15 @@ describe('QueryErrorResetBoundary', () => { const key = queryKey() function Page() { - const { data, refetch, status, fetchStatus } = useQuery( - key, - async () => { + const { data, refetch, status, fetchStatus } = useQuery({ + queryKey: key, + queryFn: async () => { throw new Error('Error') }, - { - retry: false, - enabled: false, - throwErrors: true, - }, - ) + retry: false, + enabled: false, + throwErrors: true, + }) return (
@@ -262,9 +254,9 @@ describe('QueryErrorResetBoundary', () => { let succeed = false function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -272,11 +264,9 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - throwErrors: true, - }, - ) + retry: false, + throwErrors: true, + }) return
{data}
} @@ -317,9 +307,9 @@ describe('QueryErrorResetBoundary', () => { let succeed = false function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -327,12 +317,10 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - throwErrors: true, - initialData: 'initial', - }, - ) + retry: false, + throwErrors: true, + initialData: 'initial', + }) return
{data}
} @@ -375,9 +363,9 @@ describe('QueryErrorResetBoundary', () => { let shouldReset = true function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -385,11 +373,9 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - throwErrors: true, - }, - ) + retry: false, + throwErrors: true, + }) return
{data}
} @@ -438,18 +424,16 @@ describe('QueryErrorResetBoundary', () => { let fetchCount = 0 function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) throw new Error('Error') }, - { - retry: false, - throwErrors: true, - }, - ) + retry: false, + throwErrors: true, + }) return
{data}
} @@ -494,9 +478,9 @@ describe('QueryErrorResetBoundary', () => { let renders = 0 function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) if (fetchCount > 2) { @@ -505,11 +489,9 @@ describe('QueryErrorResetBoundary', () => { throw new Error('Error') } }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) renders++ return
{data}
} @@ -577,9 +559,9 @@ describe('QueryErrorResetBoundary', () => { let succeed = false function Page() { - const { data } = useQuery( - key, - async () => { + const { data } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Error') @@ -587,11 +569,9 @@ describe('QueryErrorResetBoundary', () => { return 'data' } }, - { - retry: false, - throwErrors: true, - }, - ) + retry: false, + throwErrors: true, + }) return
{data}
} diff --git a/packages/react-query/src/__tests__/ssr-hydration.test.tsx b/packages/react-query/src/__tests__/ssr-hydration.test.tsx index 0fcc46b3d8..0ad1d3c730 100644 --- a/packages/react-query/src/__tests__/ssr-hydration.test.tsx +++ b/packages/react-query/src/__tests__/ssr-hydration.test.tsx @@ -68,7 +68,10 @@ describe('Server side rendering with de/rehydration', () => { // -- Shared part -- function SuccessComponent() { - const result = useQuery(['success'], () => fetchDataSuccess('success!')) + const result = useQuery({ + queryKey: ['success'], + queryFn: () => fetchDataSuccess('success!'), + }) return ( ) @@ -81,9 +84,10 @@ describe('Server side rendering with de/rehydration', () => { const prefetchClient = createQueryClient({ queryCache: prefetchCache, }) - await prefetchClient.prefetchQuery(['success'], () => - fetchDataSuccess('success'), - ) + await prefetchClient.prefetchQuery({ + queryKey: ['success'], + queryFn: () => fetchDataSuccess('success'), + }) const dehydratedStateServer = dehydrate(prefetchClient) const renderCache = new QueryCache() const renderClient = createQueryClient({ @@ -147,7 +151,9 @@ describe('Server side rendering with de/rehydration', () => { // -- Shared part -- function ErrorComponent() { - const result = useQuery(['error'], () => fetchDataError(), { + const result = useQuery({ + queryKey: ['error'], + queryFn: () => fetchDataError(), retry: false, }) return ( @@ -161,7 +167,10 @@ describe('Server side rendering with de/rehydration', () => { const prefetchClient = createQueryClient({ queryCache: prefetchCache, }) - await prefetchClient.prefetchQuery(['error'], () => fetchDataError()) + await prefetchClient.prefetchQuery({ + queryKey: ['error'], + queryFn: () => fetchDataError(), + }) const dehydratedStateServer = dehydrate(prefetchClient) const renderCache = new QueryCache() const renderClient = createQueryClient({ @@ -226,7 +235,10 @@ describe('Server side rendering with de/rehydration', () => { // -- Shared part -- function SuccessComponent() { - const result = useQuery(['success'], () => fetchDataSuccess('success!')) + const result = useQuery({ + queryKey: ['success'], + queryFn: () => fetchDataSuccess('success!'), + }) return ( ) diff --git a/packages/react-query/src/__tests__/ssr.test.tsx b/packages/react-query/src/__tests__/ssr.test.tsx index 4bc097de75..1aa0481e60 100644 --- a/packages/react-query/src/__tests__/ssr.test.tsx +++ b/packages/react-query/src/__tests__/ssr.test.tsx @@ -17,7 +17,7 @@ describe('Server Side Rendering', () => { const queryFn = jest.fn().mockReturnValue('data') function Page() { - const query = useQuery(key, queryFn) + const query = useQuery({ queryKey: key, queryFn }) const content = `status ${query.status}` @@ -44,9 +44,12 @@ describe('Server Side Rendering', () => { const queryClient = createQueryClient({ queryCache }) const key = queryKey() const fetchFn = () => Promise.resolve('data') - const data = await queryClient.fetchQuery(key, fetchFn) + const data = await queryClient.fetchQuery({ + queryKey: key, + queryFn: fetchFn, + }) expect(data).toBe('data') - expect(queryCache.find(key)?.state.data).toBe('data') + expect(queryCache.find({ queryKey: key })?.state.data).toBe('data') queryCache.clear() }) @@ -60,7 +63,7 @@ describe('Server Side Rendering', () => { }) function Page() { - const query = useQuery(key, queryFn) + const query = useQuery({ queryKey: key, queryFn }) const content = `status ${query.status}` @@ -71,7 +74,7 @@ describe('Server Side Rendering', () => { ) } - await queryClient.prefetchQuery(key, queryFn) + await queryClient.prefetchQuery({ queryKey: key, queryFn }) const markup = renderToString( @@ -92,7 +95,9 @@ describe('Server Side Rendering', () => { function Page() { const [page, setPage] = React.useState(1) - const { data } = useQuery([key, page], async () => page, { + const { data } = useQuery({ + queryKey: [key, page], + queryFn: async () => page, initialData: 1, }) @@ -126,7 +131,7 @@ describe('Server Side Rendering', () => { }) function Page() { - const query = useInfiniteQuery(key, queryFn) + const query = useInfiniteQuery({ queryKey: key, queryFn }) return (
    {query.data?.pages.map((page) => ( @@ -136,7 +141,7 @@ describe('Server Side Rendering', () => { ) } - await queryClient.prefetchInfiniteQuery(key, queryFn) + await queryClient.prefetchInfiniteQuery({ queryKey: key, queryFn }) const markup = renderToString( diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index af29d07a27..73515801a6 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -28,15 +28,15 @@ describe("useQuery's in Suspense mode", () => { const [stateKey, setStateKey] = React.useState(key) - const state = useQuery( - stateKey, - async () => { + const state = useQuery({ + queryKey: stateKey, + queryFn: async () => { count++ await sleep(10) return count }, - { suspense: true }, - ) + suspense: true, + }) states.push(state) @@ -72,17 +72,15 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [multiplier, setMultiplier] = React.useState(1) - const state = useInfiniteQuery( - [`${key}_${multiplier}`], - async ({ pageParam = 1 }) => { + const state = useInfiniteQuery({ + queryKey: [`${key}_${multiplier}`], + queryFn: async ({ pageParam = 1 }) => { await sleep(10) return Number(pageParam * multiplier) }, - { - suspense: true, - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + suspense: true, + getNextPageParam: (lastPage) => lastPage + 1, + }) states.push(state) return (
    @@ -127,7 +125,7 @@ describe("useQuery's in Suspense mode", () => { }) function Page() { - useQuery([key], queryFn, { suspense: true }) + useQuery({ queryKey: [key], queryFn, suspense: true }) return <>rendered } @@ -148,14 +146,14 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery( - key, - () => { + useQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { suspense: true }, - ) + suspense: true, + }) return <>rendered } @@ -177,17 +175,17 @@ describe("useQuery's in Suspense mode", () => { const rendered = renderWithClient(queryClient, ) expect(rendered.queryByText('rendered')).toBeNull() - expect(queryCache.find(key)).toBeFalsy() + expect(queryCache.find({ queryKey: key })).toBeFalsy() fireEvent.click(rendered.getByLabelText('toggle')) await waitFor(() => rendered.getByText('rendered')) - expect(queryCache.find(key)?.getObserversCount()).toBe(1) + expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(1) fireEvent.click(rendered.getByLabelText('toggle')) expect(rendered.queryByText('rendered')).toBeNull() - expect(queryCache.find(key)?.getObserversCount()).toBe(0) + expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(0) }) it('should call onSuccess on the first successful call', async () => { @@ -196,18 +194,16 @@ describe("useQuery's in Suspense mode", () => { const successFn = jest.fn() function Page() { - useQuery( - [key], - async () => { + useQuery({ + queryKey: [key], + queryFn: async () => { await sleep(10) return key }, - { - suspense: true, - select: () => 'selected', - onSuccess: successFn, - }, - ) + suspense: true, + select: () => 'selected', + onSuccess: successFn, + }) return <>rendered } @@ -232,33 +228,30 @@ describe("useQuery's in Suspense mode", () => { const successFn2 = jest.fn() function FirstComponent() { - useQuery( - key, - () => { + useQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { - suspense: true, - onSuccess: successFn1, - }, - ) + suspense: true, + onSuccess: successFn1, + }) return first } function SecondComponent() { - useQuery( - key, - () => { + useQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { - suspense: true, - onSuccess: successFn2, - }, - ) + + suspense: true, + onSuccess: successFn2, + }) return second } @@ -284,9 +277,9 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - useQuery( - key, - async () => { + useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { @@ -295,11 +288,9 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retryDelay: 10, - suspense: true, - }, - ) + retryDelay: 10, + suspense: true, + }) return
    rendered
    } @@ -349,9 +340,9 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - useQuery( - key, - async () => { + useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -359,11 +350,9 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) return
    rendered
    } @@ -410,19 +399,17 @@ describe("useQuery's in Suspense mode", () => { let count = 0 function Component() { - const result = useQuery( - key, - async () => { + const result = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(100) count++ return count }, - { - retry: false, - suspense: true, - staleTime: 0, - }, - ) + retry: false, + suspense: true, + staleTime: 0, + }) return (
    data: {result.data} @@ -468,17 +455,16 @@ describe("useQuery's in Suspense mode", () => { const key2 = queryKey() function Component(props: { queryKey: Array }) { - const result = useQuery( - props.queryKey, - async () => { + const result = useQuery({ + queryKey: props.queryKey, + queryFn: async () => { await sleep(100) return props.queryKey }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) return
    data: {result.data}
    } @@ -509,7 +495,8 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText(`data: ${key2}`)) expect( // @ts-expect-error - queryClient.getQueryCache().find(key2)!.observers[0].listeners.length, + queryClient.getQueryCache().find({ queryKey: key2 })!.observers[0] + .listeners.length, ).toBe(1) }) @@ -519,9 +506,9 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - useQuery( - key, - async () => { + useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -529,11 +516,9 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) return
    rendered
    } @@ -579,17 +564,15 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery( - key, - async (): Promise => { + useQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) throw new Error('Suspense Error a1x') }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) return
    rendered
    } @@ -619,18 +602,16 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery( - key, - async (): Promise => { + useQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) throw new Error('Suspense Error a2x') }, - { - retry: false, - suspense: true, - throwErrors: false, - }, - ) + retry: false, + suspense: true, + throwErrors: false, + }) return
    rendered
    } @@ -660,18 +641,16 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery( - key, - async (): Promise => { + useQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) return Promise.reject('Remote Error') }, - { - retry: false, - suspense: true, - throwErrors: (err) => err !== 'Local Error', - }, - ) + retry: false, + suspense: true, + throwErrors: (err) => err !== 'Local Error', + }) return
    rendered
    } @@ -701,18 +680,16 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery( - key, - async (): Promise => { + useQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) return Promise.reject('Local Error') }, - { - retry: false, - suspense: true, - throwErrors: (err) => err !== 'Local Error', - }, - ) + retry: false, + suspense: true, + throwErrors: (err) => err !== 'Local Error', + }) return
    rendered
    } @@ -749,7 +726,12 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [enabled, setEnabled] = React.useState(false) - const result = useQuery([key], queryFn, { suspense: true, enabled }) + const result = useQuery({ + queryKey: [key], + queryFn, + suspense: true, + enabled, + }) return (
    @@ -785,9 +767,9 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [nonce] = React.useState(0) const queryKeys = [`${key}-${succeed}`] - const result = useQuery( - queryKeys, - async () => { + const result = useQuery({ + queryKey: queryKeys, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -795,11 +777,9 @@ describe("useQuery's in Suspense mode", () => { return nonce } }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) return (
    rendered {result.data} @@ -851,9 +831,9 @@ describe("useQuery's in Suspense mode", () => { const [key, rerender] = React.useReducer((x) => x + 1, 0) const queryKeys = [key, succeed] - const result = useQuery( - queryKeys, - async () => { + const result = useQuery({ + queryKey: queryKeys, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -861,11 +841,9 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retry: false, - suspense: true, - }, - ) + retry: false, + suspense: true, + }) return (
    rendered {result.data} @@ -910,18 +888,17 @@ describe("useQuery's in Suspense mode", () => { const queryKeys = '1' const [enabled, setEnabled] = React.useState(false) - const result = useQuery( - [queryKeys], - async () => { + const result = useQuery({ + queryKey: [queryKeys], + queryFn: async () => { await sleep(10) throw new Error('Suspense Error Bingo') }, - { - retry: false, - suspense: true, - enabled, - }, - ) + + retry: false, + suspense: true, + enabled, + }) return (
    rendered {result.data} @@ -976,15 +953,16 @@ describe("useQuery's in Suspense mode", () => { function Page() { renders++ - state = useQuery( - key, - async () => { + state = useQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return count }, - { suspense: true, cacheTime: 0 }, - ) + suspense: true, + cacheTime: 0, + }) return (
    diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx index c6cdabd102..821bc0729f 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx @@ -48,13 +48,11 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - ({ pageParam = 0 }) => Number(pageParam), - { - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => Number(pageParam), + getNextPageParam: (lastPage) => lastPage + 1, + }) states.push(state) return null } @@ -137,20 +135,18 @@ describe('useInfiniteQuery', () => { function Page() { const start = 1 - const state = useInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { if (pageParam === 2) { throw new Error('error') } return Number(pageParam) }, - { - retry: 1, - retryDelay: 10, - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + retry: 1, + retryDelay: 10, + getNextPageParam: (lastPage) => lastPage + 1, + }) const { fetchNextPage } = state @@ -179,18 +175,17 @@ describe('useInfiniteQuery', () => { function Page() { const [order, setOrder] = React.useState('desc') - const state = useInfiniteQuery( - [key, order], - async ({ pageParam = 0 }) => { + const state = useInfiniteQuery({ + queryKey: [key, order], + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return `${pageParam}-${order}` }, - { - getNextPageParam: () => 1, - keepPreviousData: true, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: () => 1, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -274,7 +269,9 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery(key, () => ({ count: 1 }), { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: () => ({ count: 1 }), select: (data) => ({ pages: data.pages.map((x) => `count: ${x.count}`), pageParams: data.pageParams, @@ -305,7 +302,9 @@ describe('useInfiniteQuery', () => { let selectCalled = 0 function Page() { - const state = useInfiniteQuery(key, () => ({ count: 1 }), { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: () => ({ count: 1 }), select: React.useCallback((data: InfiniteData<{ count: number }>) => { selectCalled++ return { @@ -339,20 +338,18 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - async ({ pageParam = 0 }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return Number(pageParam) }, - { - select: (data) => ({ - pages: [...data.pages].reverse(), - pageParams: [...data.pageParams].reverse(), - }), - notifyOnChangeProps: 'all', - }, - ) + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), + notifyOnChangeProps: 'all', + }) states.push(state) @@ -399,17 +396,15 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = useInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(10) return Number(pageParam) }, - { - getPreviousPageParam: (firstPage) => firstPage - 1, - notifyOnChangeProps: 'all', - }, - ) + getPreviousPageParam: (firstPage) => firstPage - 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -472,9 +467,12 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery(key, async ({ pageParam = 10 }) => { - await sleep(10) - return Number(pageParam) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { + await sleep(10) + return Number(pageParam) + }, }) states.push(state) @@ -576,18 +574,17 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - async ({ pageParam = 10 }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { await sleep(10) return Number(pageParam) }, - { - getPreviousPageParam: (firstPage) => firstPage - 1, - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getPreviousPageParam: (firstPage) => firstPage - 1, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -687,17 +684,16 @@ describe('useInfiniteQuery', () => { function Page() { const multiplier = React.useRef(1) - const state = useInfiniteQuery( - key, - async ({ pageParam = 10 }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { await sleep(10) return Number(pageParam) * multiplier.current }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -776,17 +772,15 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = useInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(50) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -868,7 +862,9 @@ describe('useInfiniteQuery', () => { }) function Page() { - const { fetchNextPage } = useInfiniteQuery(key, fetchPage, { + const { fetchNextPage } = useInfiniteQuery({ + queryKey: key, + queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, }) @@ -943,7 +939,9 @@ describe('useInfiniteQuery', () => { }) function Page() { - const { fetchNextPage } = useInfiniteQuery(key, fetchPage, { + const { fetchNextPage } = useInfiniteQuery({ + queryKey: key, + queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, }) @@ -993,17 +991,15 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = useInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(50) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1046,20 +1042,18 @@ describe('useInfiniteQuery', () => { const initialData = { pages: [1, 2, 3, 4], pageParams: [0, 1, 2, 3] } function List() { - useInfiniteQuery( - key, - async ({ pageParam = 0, signal: _ }) => { + useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0, signal: _ }) => { fetches++ await sleep(50) return Number(pageParam) * 10 }, - { - initialData, - getNextPageParam: (_, allPages) => { - return allPages.length === 4 ? undefined : allPages.length - }, + initialData, + getNextPageParam: (_, allPages) => { + return allPages.length === 4 ? undefined : allPages.length }, - ) + }) return null } @@ -1093,17 +1087,15 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - async ({ pageParam = 0 }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1171,17 +1163,15 @@ describe('useInfiniteQuery', () => { function Page() { const [firstPage, setFirstPage] = React.useState(0) - const state = useInfiniteQuery( - key, - async ({ pageParam = firstPage }) => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = firstPage }) => { await sleep(10) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1252,18 +1242,16 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - async ({ pageParam }): Promise => { + const state = useInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam }): Promise => { await sleep(10) return pageParam }, - { - initialData: { pages: [1], pageParams: [1] }, - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + initialData: { pages: [1], pageParams: [1] }, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1318,13 +1306,11 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - ({ pageParam = 1 }) => Number(pageParam), - { - getNextPageParam: () => undefined, - }, - ) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 1 }) => Number(pageParam), + getNextPageParam: () => undefined, + }) states.push(state) @@ -1357,14 +1343,12 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - ({ pageParam = 10 }): number => pageParam, - { - initialData: { pages: [10], pageParams: [undefined] }, - getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), - }, - ) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }): number => pageParam, + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), + }) states.push(state) @@ -1397,14 +1381,13 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - ({ pageParam = 10 }): number => pageParam, - { - initialData: { pages: [10], pageParams: [undefined] }, - getNextPageParam: () => undefined, - }, - ) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }): number => pageParam, + + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: () => undefined, + }) states.push(state) @@ -1437,17 +1420,15 @@ describe('useInfiniteQuery', () => { const states: UseInfiniteQueryResult[] = [] function Page() { - const state = useInfiniteQuery( - key, - ({ pageParam = 1 }) => Number(pageParam), - { - getNextPageParam: (lastPage) => (lastPage === 1 ? 2 : false), - select: (data) => ({ - pages: data.pages.map((x) => x.toString()), - pageParams: data.pageParams, - }), - }, - ) + const state = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 1 }) => Number(pageParam), + getNextPageParam: (lastPage) => (lastPage === 1 ? 2 : false), + select: (data) => ({ + pages: data.pages.map((x) => x.toString()), + pageParams: data.pageParams, + }), + }) states.push(state) @@ -1502,14 +1483,13 @@ describe('useInfiniteQuery', () => { fetchNextPage, hasNextPage, refetch, - } = useInfiniteQuery( - key, - ({ pageParam = 0 }) => + } = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => fetchItemsWithLimit(pageParam, fetchCountRef.current++), - { - getNextPageParam: (lastPage) => lastPage.nextId, - }, - ) + + getNextPageParam: (lastPage) => lastPage.nextId, + }) return (
    @@ -1551,7 +1531,7 @@ describe('useInfiniteQuery', () => { // makes an actual network request // and calls invalidateQueries in an onSuccess items.splice(4, 1) - queryClient.invalidateQueries(key) + queryClient.invalidateQueries({ queryKey: key }) }} > Remove item @@ -1628,18 +1608,16 @@ describe('useInfiniteQuery', () => { fetchNextPage, hasNextPage, refetch, - } = useInfiniteQuery( - key, - ({ pageParam = 0 }) => + } = useInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => fetchItems( pageParam, fetchCountRef.current++, pageParam === MAX || (pageParam === MAX - 1 && isRemovedLastPage), ), - { - getNextPageParam: (lastPage) => lastPage.nextId, - }, - ) + getNextPageParam: (lastPage) => lastPage.nextId, + }) return (
    @@ -1756,7 +1734,7 @@ describe('useInfiniteQuery', () => { } function Inner() { - const state = useInfiniteQuery(key, queryFn) + const state = useInfiniteQuery({ queryKey: key, queryFn }) return (

    Status: {state.status}

    diff --git a/packages/react-query/src/__tests__/useIsFetching.test.tsx b/packages/react-query/src/__tests__/useIsFetching.test.tsx index d94cd91064..54063daa79 100644 --- a/packages/react-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/react-query/src/__tests__/useIsFetching.test.tsx @@ -27,16 +27,14 @@ describe('useIsFetching', () => { function Query() { const [ready, setReady] = React.useState(false) - useQuery( - key, - async () => { + useQuery({ + queryKey: key, + queryFn: async () => { await sleep(50) return 'test' }, - { - enabled: ready, - }, - ) + enabled: ready, + }) return } @@ -74,17 +72,23 @@ describe('useIsFetching', () => { } function FirstQuery() { - useQuery(key1, async () => { - await sleep(100) - return 'data' + useQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(100) + return 'data' + }, }) return null } function SecondQuery() { - useQuery(key2, async () => { - await sleep(100) - return 'data' + useQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(100) + return 'data' + }, }) return null } @@ -119,24 +123,30 @@ describe('useIsFetching', () => { const isFetchings: number[] = [] function One() { - useQuery(key1, async () => { - await sleep(10) - return 'test' + useQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return null } function Two() { - useQuery(key2, async () => { - await sleep(20) - return 'test' + useQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(20) + return 'test' + }, }) return null } function Page() { const [started, setStarted] = React.useState(false) - const isFetching = useIsFetching(key1) + const isFetching = useIsFetching({ queryKey: key1 }) isFetchings.push(isFetching) @@ -177,17 +187,15 @@ describe('useIsFetching', () => { const isFetching = useIsFetching(undefined, { context: context }) - useQuery( - key, - async () => { + useQuery({ + queryKey: key, + queryFn: async () => { await sleep(50) return 'test' }, - { - enabled: ready, - context, - }, - ) + enabled: ready, + context, + }) return (
    @@ -221,7 +229,9 @@ describe('useIsFetching', () => { function Page() { const isFetching = useIsFetching() - useQuery(key, async () => 'test', { + useQuery({ + queryKey: key, + queryFn: async () => 'test', enabled: true, context, throwErrors: true, @@ -253,9 +263,12 @@ describe('useIsFetching', () => { const key = queryKey() function Page() { - useQuery(key, async () => { - await sleep(10) - return 'test' + useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) const isFetching = useIsFetching() diff --git a/packages/react-query/src/__tests__/useIsMutating.test.tsx b/packages/react-query/src/__tests__/useIsMutating.test.tsx index 67940e7b85..811cb45ceb 100644 --- a/packages/react-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/react-query/src/__tests__/useIsMutating.test.tsx @@ -24,13 +24,19 @@ describe('useIsMutating', () => { } function Mutations() { - const { mutate: mutate1 } = useMutation(['mutation1'], async () => { - await sleep(150) - return 'data' + const { mutate: mutate1 } = useMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(150) + return 'data' + }, }) - const { mutate: mutate2 } = useMutation(['mutation2'], async () => { - await sleep(50) - return 'data' + const { mutate: mutate2 } = useMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(50) + return 'data' + }, }) React.useEffect(() => { @@ -61,19 +67,25 @@ describe('useIsMutating', () => { const queryClient = createQueryClient() function IsMutating() { - const isMutating = useIsMutating(['mutation1']) + const isMutating = useIsMutating({ mutationKey: ['mutation1'] }) isMutatings.push(isMutating) return null } function Page() { - const { mutate: mutate1 } = useMutation(['mutation1'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate1 } = useMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) - const { mutate: mutate2 } = useMutation(['mutation2'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate2 } = useMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) React.useEffect(() => { @@ -102,13 +114,19 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = useMutation(['mutation1'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate1 } = useMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) - const { mutate: mutate2 } = useMutation(['mutation2'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate2 } = useMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) React.useEffect(() => { @@ -148,9 +166,12 @@ describe('useIsMutating', () => { function Page() { const [mounted, setMounted] = React.useState(true) - const { mutate: mutate1 } = useMutation(['mutation1'], async () => { - await sleep(10) - return 'data' + const { mutate: mutate1 } = useMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(10) + return 'data' + }, }) React.useEffect(() => { @@ -189,22 +210,22 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = useMutation( - ['mutation1'], - async () => { + const { mutate: mutate1 } = useMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { await sleep(150) return 'data' }, - { context }, - ) - const { mutate: mutate2 } = useMutation( - ['mutation2'], - async () => { + context, + }) + const { mutate: mutate2 } = useMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { await sleep(50) return 'data' }, - { context }, - ) + context, + }) React.useEffect(() => { mutate1() @@ -233,7 +254,9 @@ describe('useIsMutating', () => { } function Page() { - const { mutate } = useMutation(['mutation'], async () => 'data', { + const { mutate } = useMutation({ + mutationKey: ['mutation'], + mutationFn: async () => 'data', throwErrors: true, context, }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 78b77fc740..9f15b2e262 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -26,7 +26,7 @@ describe('useMutation', () => { mutate, data = 'empty', reset, - } = useMutation(() => Promise.resolve('mutation')) + } = useMutation({ mutationFn: () => Promise.resolve('mutation') }) return (
    @@ -56,10 +56,12 @@ describe('useMutation', () => { it('should be able to reset `error`', async () => { function Page() { - const { mutate, error, reset } = useMutation(() => { - const err = new Error('Expected mock error. All is well!') - err.stack = '' - return Promise.reject(err) + const { mutate, error, reset } = useMutation({ + mutationFn: () => { + const err = new Error('Expected mock error. All is well!') + err.stack = '' + return Promise.reject(err) + }, }) return ( @@ -98,17 +100,16 @@ describe('useMutation', () => { const onSettledMock = jest.fn() function Page() { - const { mutate } = useMutation( - (vars: { count: number }) => Promise.resolve(vars.count), - { - onSuccess: (data) => { - onSuccessMock(data) - }, - onSettled: (data) => { - onSettledMock(data) - }, + const { mutate } = useMutation({ + mutationFn: (vars: { count: number }) => Promise.resolve(vars.count), + + onSuccess: (data) => { + onSuccessMock(data) }, - ) + onSettled: (data) => { + onSettledMock(data) + }, + }) return (
    @@ -167,7 +168,7 @@ describe('useMutation', () => { Value, string, Value - >(mutateFn) + >({ mutationFn: mutateFn }) return (
    @@ -204,23 +205,21 @@ describe('useMutation', () => { let count = 0 function Page() { - const { mutate } = useMutation( - (vars: { count: number }) => { + const { mutate } = useMutation({ + mutationFn: (vars: { count: number }) => { const error = new Error( `Expected mock error. All is well! ${vars.count}`, ) error.stack = '' return Promise.reject(error) }, - { - onError: (error: Error) => { - onErrorMock(error.message) - }, - onSettled: (_data, error) => { - onSettledMock(error?.message) - }, + onError: (error: Error) => { + onErrorMock(error.message) }, - ) + onSettled: (_data, error) => { + onSettledMock(error?.message) + }, + }) return (
    @@ -273,7 +272,8 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const { mutateAsync } = useMutation(async (text: string) => text, { + const { mutateAsync } = useMutation({ + mutationFn: async (text: string) => text, onSuccess: async () => { callbacks.push('useMutation.onSuccess') }, @@ -318,17 +318,15 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const { mutateAsync } = useMutation( - async (_text: string) => Promise.reject('oops'), - { - onError: async () => { - callbacks.push('useMutation.onError') - }, - onSettled: async () => { - callbacks.push('useMutation.onSettled') - }, + const { mutateAsync } = useMutation({ + mutationFn: async (_text: string) => Promise.reject('oops'), + onError: async () => { + callbacks.push('useMutation.onError') }, - ) + onSettled: async () => { + callbacks.push('useMutation.onSettled') + }, + }) React.useEffect(() => { setActTimeout(async () => { @@ -376,7 +374,7 @@ describe('useMutation', () => { const states: UseMutationResult[] = [] function Page() { - const state = useMutation(key) + const state = useMutation({ mutationKey: key }) states.push(state) @@ -405,16 +403,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const { mutate } = useMutation( - (_text: string) => { + const { mutate } = useMutation({ + mutationFn: (_text: string) => { count++ return Promise.reject('oops') }, - { - retry: 1, - retryDelay: 5, - }, - ) + retry: 1, + retryDelay: 5, + }) React.useEffect(() => { setActTimeout(() => { @@ -438,16 +434,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = useMutation( - (_text: string) => { + const mutation = useMutation({ + mutationFn: (_text: string) => { count++ return Promise.reject(new Error('oops')) }, - { - retry: 1, - retryDelay: 5, - }, - ) + retry: 1, + retryDelay: 5, + }) return (
    @@ -501,16 +495,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - { - onMutate, - }, - ) + onMutate, + }) return (
    @@ -551,10 +543,12 @@ describe('useMutation', () => { const states: Array = [] function Page() { - const mutation = useMutation(async (_text: string) => { - count++ - await sleep(10) - return count + const mutation = useMutation({ + mutationFn: async (_text: string) => { + count++ + await sleep(10) + return count + }, }) states.push(`${mutation.status}, ${mutation.isPaused}`) @@ -597,18 +591,16 @@ describe('useMutation', () => { const states: UseMutationResult[] = [] function Page() { - const state = useMutation( - async (_text: string) => { + const state = useMutation({ + mutationFn: async (_text: string) => { await sleep(1) count++ return count > 1 ? Promise.resolve('data') : Promise.reject('oops') }, - { - retry: 1, - retryDelay: 5, - networkMode: 'offlineFirst', - }, - ) + retry: 1, + retryDelay: 5, + networkMode: 'offlineFirst', + }) states.push(state) @@ -678,7 +670,7 @@ describe('useMutation', () => { it('should not change state if unmounted', async () => { function Mutates() { - const { mutate } = useMutation(() => sleep(10)) + const { mutate } = useMutation({ mutationFn: () => sleep(10) }) return } function Page() { @@ -698,14 +690,14 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is set to true', async () => { function Page() { - const { mutate } = useMutation( - () => { + const { mutate } = useMutation({ + mutationFn: () => { const err = new Error('Expected mock error. All is well!') err.stack = '' return Promise.reject(err) }, - { throwErrors: true }, - ) + throwErrors: true, + }) return (
    @@ -737,19 +729,17 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is a function that returns true', async () => { let boundary = false function Page() { - const { mutate, error } = useMutation( - () => { + const { mutate, error } = useMutation({ + mutationFn: () => { const err = new Error('mock error') err.stack = '' return Promise.reject(err) }, - { - throwErrors: () => { - boundary = !boundary - return !boundary - }, + throwErrors: () => { + boundary = !boundary + return !boundary }, - ) + }) return (
    @@ -804,17 +794,16 @@ describe('useMutation', () => { const metaErrorMessage = 'mutation failed' function Page() { - const { mutate: succeed, isSuccess } = useMutation(async () => '', { + const { mutate: succeed, isSuccess } = useMutation({ + mutationFn: async () => '', meta: { metaSuccessMessage }, }) - const { mutate: error, isError } = useMutation( - async () => { + const { mutate: error, isError } = useMutation({ + mutationFn: async () => { throw new Error('') }, - { - meta: { metaErrorMessage }, - }, - ) + meta: { metaErrorMessage }, + }) return (
    @@ -864,19 +853,17 @@ describe('useMutation', () => { } function Component() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - { - mutationKey, - cacheTime: 0, - onSuccess, - onSettled, - }, - ) + mutationKey, + cacheTime: 0, + onSuccess, + onSettled, + }) return (
    @@ -928,7 +915,10 @@ describe('useMutation', () => { mutate, data = 'empty', reset, - } = useMutation(() => Promise.resolve('mutation'), { context }) + } = useMutation({ + mutationFn: () => Promise.resolve('mutation'), + context, + }) return (
    @@ -960,7 +950,9 @@ describe('useMutation', () => { const context = React.createContext(undefined) function Page() { - const { data = '' } = useMutation(() => Promise.resolve('mutation')) + const { data = '' } = useMutation({ + mutationFn: () => Promise.resolve('mutation'), + }) return (
    @@ -989,17 +981,15 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return `result${count}` }, - { - onSuccess, - onSettled, - }, - ) + onSuccess, + onSettled, + }) return (
    @@ -1049,16 +1039,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { await sleep(10) return 'result' }, - { - onSuccess: () => Promise.reject(error), - onError, - }, - ) + onSuccess: () => Promise.reject(error), + onError, + }) return (
    @@ -1084,15 +1072,13 @@ describe('useMutation', () => { const mutateFnError = new Error('mutateFnError') function Page() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, - { - onError: () => Promise.reject(error), - }, - ) + onError: () => Promise.reject(error), + }) return (
    @@ -1121,16 +1107,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = useMutation( - async (_text: string) => { + const mutation = useMutation({ + mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, - { - onSettled: () => Promise.reject(error), - onError, - }, - ) + onSettled: () => Promise.reject(error), + onError, + }) return (
    diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index ec60ff9baa..737113c57c 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -34,59 +34,68 @@ describe('useQuery', () => { // eslint-disable-next-line function Page() { // unspecified query function should default to unknown - const noQueryFn = useQuery(key) + const noQueryFn = useQuery({ queryKey: key }) expectType(noQueryFn.data) expectType(noQueryFn.error) // it should infer the result type from the query function - const fromQueryFn = useQuery(key, () => 'test') + const fromQueryFn = useQuery({ queryKey: key, queryFn: () => 'test' }) expectType(fromQueryFn.data) expectType(fromQueryFn.error) // it should be possible to specify the result type - const withResult = useQuery(key, () => 'test') + const withResult = useQuery({ + queryKey: key, + queryFn: () => 'test', + }) expectType(withResult.data) expectType(withResult.error) // it should be possible to specify the error type - const withError = useQuery(key, () => 'test') + const withError = useQuery({ + queryKey: key, + queryFn: () => 'test', + }) expectType(withError.data) expectType(withError.error) // it should provide the result type in the configuration - useQuery([key], async () => true, { + useQuery({ + queryKey: [key], + queryFn: async () => true, onSuccess: (data) => expectType(data), onSettled: (data) => expectType(data), }) // it should be possible to specify a union type as result type - const unionTypeSync = useQuery( - key, - () => (Math.random() > 0.5 ? 'a' : 'b'), - { - onSuccess: (data) => expectType<'a' | 'b'>(data), - }, - ) + const unionTypeSync = useQuery({ + queryKey: key, + queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'), + + onSuccess: (data) => expectType<'a' | 'b'>(data), + }) expectType<'a' | 'b' | undefined>(unionTypeSync.data) - const unionTypeAsync = useQuery<'a' | 'b'>( - key, - () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), - { - onSuccess: (data) => expectType<'a' | 'b'>(data), - }, - ) + const unionTypeAsync = useQuery<'a' | 'b'>({ + queryKey: key, + queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), + + onSuccess: (data) => expectType<'a' | 'b'>(data), + }) expectType<'a' | 'b' | undefined>(unionTypeAsync.data) // should error when the query function result does not match with the specified type // @ts-expect-error - useQuery(key, () => 'test') + useQuery({ queryKey: key, queryFn: () => 'test' }) // it should infer the result type from a generic query function function queryFn(): Promise { return Promise.resolve({} as T) } - const fromGenericQueryFn = useQuery(key, () => queryFn()) + const fromGenericQueryFn = useQuery({ + queryKey: key, + queryFn: () => queryFn(), + }) expectType(fromGenericQueryFn.data) expectType(fromGenericQueryFn.error) @@ -124,9 +133,10 @@ describe('useQuery', () => { }) // it should handle query-functions that return Promise - useQuery(key, () => - fetch('return Promise').then((resp) => resp.json()), - ) + useQuery({ + queryKey: key, + queryFn: () => fetch('return Promise').then((resp) => resp.json()), + }) // handles wrapped queries with custom fetcher passed as inline queryFn const useWrappedQuery = < @@ -145,7 +155,12 @@ describe('useQuery', () => { UseQueryOptions, 'queryKey' | 'queryFn' | 'initialData' >, - ) => useQuery(qk, () => fetcher(qk[1], 'token'), options) + ) => + useQuery({ + queryKey: qk, + queryFn: () => fetcher(qk[1], 'token'), + ...options, + }) const test = useWrappedQuery([''], async () => '1') expectType(test.data) @@ -162,7 +177,7 @@ describe('useQuery', () => { UseQueryOptions, 'queryKey' | 'queryFn' | 'initialData' >, - ) => useQuery(qk, fetcher, options) + ) => useQuery({ queryKey: qk, queryFn: fetcher, ...options }) const testFuncStyle = useWrappedFuncStyleQuery([''], async () => true) expectType(testFuncStyle.data) } @@ -173,9 +188,12 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { data = 'default' } = useQuery(key, async () => { - await sleep(10) - return 'test' + const { data = 'default' } = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -197,9 +215,12 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'test' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) states.push(state) @@ -288,14 +309,13 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery( - key, - () => Promise.reject('rejected'), - { - retry: 1, - retryDelay: 1, - }, - ) + const state = useQuery({ + queryKey: key, + queryFn: () => Promise.reject('rejected'), + + retry: 1, + retryDelay: 1, + }) states.push(state) @@ -397,10 +417,13 @@ describe('useQuery', () => { it('should set isFetchedAfterMount to true after a query has been fetched', async () => { const key = queryKey() - await queryClient.prefetchQuery(key, () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'prefetched', + }) function Page() { - const result = useQuery(key, () => 'new data') + const result = useQuery({ queryKey: key, queryFn: () => 'new data' }) return ( <> @@ -432,14 +455,14 @@ describe('useQuery', () => { const onSuccess = jest.fn() function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data' }, - { onSuccess }, - ) + onSuccess, + }) states.push(state) return
    data: {state.data}
    } @@ -459,15 +482,15 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return 'data' + count }, - { onSuccess }, - ) + onSuccess, + }) states.push(state) @@ -496,7 +519,12 @@ describe('useQuery', () => { const onSuccess = jest.fn() function Page() { - const state = useQuery(key, () => 'data', { enabled: false, onSuccess }) + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', + enabled: false, + onSuccess, + }) states.push(state) @@ -540,14 +568,14 @@ describe('useQuery', () => { } function Component() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data' }, - { onSuccess }, - ) + onSuccess, + }) states.push(state) return null } @@ -565,7 +593,9 @@ describe('useQuery', () => { const onError = jest.fn() function Page() { - const state = useQuery(key, () => Promise.reject('error'), { + const state = useQuery({ + queryKey: key, + queryFn: () => Promise.reject('error'), retry: false, onError, }) @@ -586,16 +616,15 @@ describe('useQuery', () => { const onError = jest.fn() function Page() { - const { status, fetchStatus } = useQuery( - key, - async () => { + const { status, fetchStatus } = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 23 }, - { - onError, - }, - ) + + onError, + }) return ( status: {status}, fetchStatus: {fetchStatus} @@ -607,7 +636,7 @@ describe('useQuery', () => { rendered.getByText('status: loading, fetchStatus: fetching') - await queryClient.cancelQueries(key) + await queryClient.cancelQueries({ queryKey: key }) // query cancellation will reset the query to it's initial state await waitFor(() => rendered.getByText('status: loading, fetchStatus: idle'), @@ -621,7 +650,11 @@ describe('useQuery', () => { const onSettled = jest.fn() function Page() { - const state = useQuery(key, () => 'data', { onSettled }) + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', + onSettled, + }) states.push(state) return
    data: {state.data}
    @@ -644,7 +677,9 @@ describe('useQuery', () => { const onSettled = jest.fn() function Page() { - const state = useQuery(key, () => Promise.reject('error'), { + const state = useQuery({ + queryKey: key, + queryFn: () => Promise.reject('error'), retry: false, onSettled, }) @@ -665,15 +700,16 @@ describe('useQuery', () => { let fetchCount = 0 function Page() { - const { refetch } = useQuery( - key, - async () => { + const { refetch } = useQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false, initialData: 'initialData' }, - ) + enabled: false, + initialData: 'initialData', + }) React.useEffect(() => { setActTimeout(() => { @@ -699,15 +735,16 @@ describe('useQuery', () => { let fetchCount = 0 function Page() { - const { refetch } = useQuery( - key, - async () => { + const { refetch } = useQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false, initialData: 'initialData' }, - ) + enabled: false, + initialData: 'initialData', + }) React.useEffect(() => { setActTimeout(() => { @@ -733,15 +770,15 @@ describe('useQuery', () => { let fetchCount = 0 function Page() { - const { refetch } = useQuery( - key, - async () => { + const { refetch } = useQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false }, - ) + enabled: false, + }) React.useEffect(() => { setActTimeout(() => { @@ -769,7 +806,7 @@ describe('useQuery', () => { queryClient.setQueryDefaults(key, { queryFn: () => 'data' }) function Page() { - const state = useQuery(key) + const state = useQuery({ queryKey: key }) states.push(state) return null } @@ -803,17 +840,16 @@ describe('useQuery', () => { } function Component({ value }: { value: string }) { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data: ' + value }, - { - cacheTime: 0, - notifyOnChangeProps: 'all', - }, - ) + + cacheTime: 0, + notifyOnChangeProps: 'all', + }) states.push(state) return (
    @@ -864,17 +900,16 @@ describe('useQuery', () => { function Page() { const [, rerender] = React.useState({}) - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(5) return 'data' }, - { - cacheTime: 0, - notifyOnChangeProps: 'all', - }, - ) + + cacheTime: 0, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -927,7 +962,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'test', refetchOnMount: false, }) states.push(state) @@ -950,7 +987,9 @@ describe('useQuery', () => { queryClient.setQueryData(key, 'prefetched') function Page() { - const state = useQuery(key, () => 'test', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'test', refetchOnMount: false, }) states.push(state) @@ -970,7 +1009,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => ({ name: 'test' }), { + const state = useQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: (data) => data.name, }) states.push(state) @@ -1020,7 +1061,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => ({ name: 'test' }), { + const state = useQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: (data) => data.name, notifyOnChangeProps: ['data'], }) @@ -1061,7 +1104,9 @@ describe('useQuery', () => { const error = new Error('Select Error') function Page() { - const state = useQuery(key, () => ({ name: 'test' }), { + const state = useQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: () => { throw error }, @@ -1091,16 +1136,15 @@ describe('useQuery', () => { function Page() { const [, rerender] = React.useReducer(() => ({}), {}) - const state = useQuery( - key, - () => (runs === 0 ? 'test' : 'test2'), - { - select: React.useCallback(() => { - runs++ - throw error - }, []), - }, - ) + const state = useQuery({ + queryKey: key, + queryFn: () => (runs === 0 ? 'test' : 'test2'), + + select: React.useCallback(() => { + runs++ + throw error + }, []), + }) return (
    error: {state.error?.message}
    @@ -1127,9 +1171,12 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'test' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) states.push(state) @@ -1166,7 +1213,7 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test') + const state = useQuery({ queryKey: key, queryFn: () => 'test' }) states.push(state) @@ -1203,7 +1250,11 @@ describe('useQuery', () => { function Page() { const [, rerender] = React.useState({}) - const state = useQuery(key, () => ++count, { notifyOnChangeProps: 'all' }) + const state = useQuery({ + queryKey: key, + queryFn: () => ++count, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1244,14 +1295,14 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return ++count }, - { notifyOnChangeProps: 'all' }, - ) + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1306,15 +1357,15 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count === 1 ? result1 : result2 }, - { notifyOnChangeProps: 'all' }, - ) + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1361,24 +1412,23 @@ describe('useQuery', () => { queryClient.setQueryData(key, 'set') function Page() { - const result = useQuery( - key, - async () => { + const result = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'fetched' }, - { - initialData: 'initial', - staleTime: Infinity, - }, - ) + + initialData: 'initial', + staleTime: Infinity, + }) results.push(result) return (
    isFetching: {result.isFetching}
    - data: {result.data} @@ -1405,21 +1455,24 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity, notifyOnChangeProps: 'all' }, - ) + staleTime: Infinity, + notifyOnChangeProps: 'all', + }) states.push(state) return (
    - data: {state.data} @@ -1471,15 +1524,15 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { enabled: false }, - ) + enabled: false, + }) states.push(state) @@ -1511,21 +1564,21 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { enabled: false }, - ) + enabled: false, + }) states.push(state) React.useEffect(() => { setActTimeout(() => { - queryClient.invalidateQueries(key) + queryClient.invalidateQueries({ queryKey: key }) }, 20) }, []) @@ -1552,14 +1605,14 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(5) return count }, - { enabled: count === 0 }, - ) + enabled: count === 0, + }) states.push(state) @@ -1615,14 +1668,14 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(10) return count }, - { keepPreviousData: true }, - ) + keepPreviousData: true, + }) states.push(state) @@ -1677,20 +1730,19 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page({ count }: { count: number }) { - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(10) if (count === 2) { throw new Error('Error test') } return Promise.resolve(count) }, - { - retry: false, - keepPreviousData: true, - }, - ) + + retry: false, + keepPreviousData: true, + }) states.push(state) @@ -1784,14 +1836,15 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(10) return count }, - { initialData: 99, keepPreviousData: true }, - ) + initialData: 99, + keepPreviousData: true, + }) states.push(state) @@ -1864,14 +1917,16 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(10) return count }, - { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, - ) + enabled: false, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -1968,14 +2023,16 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(10) - const state = useQuery( - [key, count], - async () => { + const state = useQuery({ + queryKey: [key, count], + queryFn: async () => { await sleep(10) return count }, - { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, - ) + enabled: false, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -2044,14 +2101,14 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function FirstComponent() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 1 }, - { notifyOnChangeProps: 'all' }, - ) + notifyOnChangeProps: 'all', + }) const refetch = state.refetch states.push(state) @@ -2065,7 +2122,7 @@ describe('useQuery', () => { } function SecondComponent() { - useQuery(key, () => 2, { notifyOnChangeProps: 'all' }) + useQuery({ queryKey: key, queryFn: () => 2, notifyOnChangeProps: 'all' }) return null } @@ -2105,39 +2162,40 @@ describe('useQuery', () => { const states1: UseQueryResult[] = [] const states2: UseQueryResult[] = [] - await queryClient.prefetchQuery(key, async () => { - await sleep(10) - return 'prefetch' + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'prefetch' + }, }) await sleep(20) function FirstComponent() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'one' }, - { - staleTime: 100, - }, - ) + + staleTime: 100, + }) states1.push(state) return null } function SecondComponent() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'two' }, - { - staleTime: 10, - }, - ) + + staleTime: 10, + }) states2.push(state) return null } @@ -2205,7 +2263,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'test', staleTime: 50, }) states.push(state) @@ -2227,16 +2287,15 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(5) return 'test' }, - { - notifyOnChangeProps: ['data'], - }, - ) + + notifyOnChangeProps: ['data'], + }) states.push(state) @@ -2285,12 +2344,16 @@ describe('useQuery', () => { const key2 = queryKey() function Page() { - const first = useQuery(key1, () => 'data', { + const first = useQuery({ + queryKey: key1, + queryFn: () => 'data', enabled: false, initialData: 'init', }) - const second = useQuery(key2, () => 'data', { + const second = useQuery({ + queryKey: key2, + queryFn: () => 'data', enabled: false, initialData: 'init', }) @@ -2327,14 +2390,14 @@ describe('useQuery', () => { } function Page() { - useQuery(key, queryFn1) - useQuery(key, queryFn2) + useQuery({ queryKey: key, queryFn: queryFn1 }) + useQuery({ queryKey: key, queryFn: queryFn2 }) return null } renderWithClient(queryClient, ) - expect(queryCache.find(key)!.options.queryFn).toBe(queryFn1) + expect(queryCache.find({ queryKey: key })!.options.queryFn).toBe(queryFn1) }) it('should batch re-renders', async () => { @@ -2348,8 +2411,8 @@ describe('useQuery', () => { } function Page() { - const query1 = useQuery(key, queryFn) - const query2 = useQuery(key, queryFn) + const query1 = useQuery({ queryKey: key, queryFn }) + const query2 = useQuery({ queryKey: key, queryFn }) renders++ return ( @@ -2382,12 +2445,16 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - useQuery(key, queryFn, { + useQuery({ + queryKey: key, + queryFn, onSuccess: () => { setCount((x) => x + 1) }, }) - useQuery(key, queryFn, { + useQuery({ + queryKey: key, + queryFn, onSuccess: () => { setCount((x) => x + 1) }, @@ -2417,7 +2484,7 @@ describe('useQuery', () => { function Page() { const [, setNewState] = React.useState('state') - const state = useQuery(key, () => 'data') + const state = useQuery({ queryKey: key, queryFn: () => 'data' }) React.useEffect(() => { setActTimeout(() => { queryClient.setQueryData(key, 'new') @@ -2439,10 +2506,12 @@ describe('useQuery', () => { const key2 = queryKey() function Page() { - const first = useQuery(key1, () => 'data', { + const first = useQuery({ + queryKey: key1, + queryFn: () => 'data', enabled: false, }) - const second = useQuery(key2, () => 'data') + const second = useQuery({ queryKey: key2, queryFn: () => 'data' }) return (
    @@ -2470,9 +2539,12 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status } = useQuery(key, async () => { - await sleep(10) - return 'test' + const { status } = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return
    status: {status}
    @@ -2496,7 +2568,7 @@ describe('useQuery', () => { } function Page() { - const state = useQuery([key, variables], queryFn) + const state = useQuery({ queryKey: [key, variables], queryFn }) states.push(state) return
    {state.status}
    } @@ -2515,7 +2587,9 @@ describe('useQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Page() { - const { data = 'default' } = useQuery(key, queryFn, { + const { data = 'default' } = useQuery({ + queryKey: key, + queryFn, enabled: false, }) @@ -2543,7 +2617,9 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery(key, () => count++, { + const state = useQuery({ + queryKey: key, + queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: false, }) @@ -2572,7 +2648,9 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery(key, () => count++, { + const state = useQuery({ + queryKey: key, + queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: () => false, }) @@ -2601,7 +2679,9 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery(key, () => count++, { + const state = useQuery({ + queryKey: key, + queryFn: () => count++, staleTime: Infinity, refetchOnWindowFocus: true, }) @@ -2630,17 +2710,16 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - staleTime: Infinity, - refetchOnWindowFocus: 'always', - }, - ) + + staleTime: Infinity, + refetchOnWindowFocus: 'always', + }) states.push(state) return null } @@ -2668,18 +2747,17 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - staleTime: 0, - retry: 0, - refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, - }, - ) + + staleTime: 0, + retry: 0, + refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, + }) states.push(state) return
    data: {String(state.data)}
    } @@ -2718,10 +2796,15 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] - await queryClient.prefetchQuery(key, () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'prefetched', + }) function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: 'always', staleTime: Infinity, }) @@ -2750,12 +2833,17 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] - await queryClient.prefetchQuery(key, () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'prefetched', + }) await sleep(10) function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: true, staleTime: 0, }) @@ -2784,13 +2872,13 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery( - key, - () => { + const { status, error } = useQuery({ + queryKey: key, + queryFn: () => { return Promise.reject('Error test jaylen') }, - { retry: false }, - ) + retry: false, + }) return (
    @@ -2810,11 +2898,12 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery( - key, - () => Promise.reject('Error test jaylen'), - { retry: false, throwErrors: true }, - ) + const { status, error } = useQuery({ + queryKey: key, + queryFn: () => Promise.reject('Error test jaylen'), + retry: false, + throwErrors: true, + }) return (
    @@ -2840,7 +2929,9 @@ describe('useQuery', () => { let result: UseQueryResult | undefined function Page() { - const query = useQuery(key, () => Promise.resolve('data'), { + const query = useQuery({ + queryKey: key, + queryFn: () => Promise.resolve('data'), throwErrors: true, }) @@ -2864,14 +2955,13 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery( - key, - () => Promise.reject('Local Error'), - { - retry: false, - throwErrors: (err) => err !== 'Local Error', - }, - ) + const { status, error } = useQuery({ + queryKey: key, + queryFn: () => Promise.reject('Local Error'), + + retry: false, + throwErrors: (err) => err !== 'Local Error', + }) return (
    @@ -2896,14 +2986,13 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery( - key, - () => Promise.reject(new Error('Remote Error')), - { - retry: false, - throwErrors: (err) => err.message !== 'Local Error', - }, - ) + const { status, error } = useQuery({ + queryKey: key, + queryFn: () => Promise.reject(new Error('Remote Error')), + + retry: false, + throwErrors: (err) => err.message !== 'Local Error', + }) return (
    @@ -2937,18 +3026,17 @@ describe('useQuery', () => { let count = 0 function Page() { - const result = useQuery( - key, - async () => { + const result = useQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return Promise.reject('some error') }, - { - retry: 2, - retryDelay: 100, - }, - ) + + retry: 2, + retryDelay: 100, + }) return (
    @@ -2987,18 +3075,17 @@ describe('useQuery', () => { let count = 0 function Page() { - const result = useQuery( - key, - async () => { + const result = useQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return Promise.reject('some error') }, - { - retry: 2, - retryDelay: 100, - }, - ) + + retry: 2, + retryDelay: 100, + }) return (
    @@ -3041,10 +3128,15 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] - await queryClient.prefetchQuery(key, () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => 'prefetched', + }) function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: 'always', staleTime: 50, }) @@ -3085,7 +3177,9 @@ describe('useQuery', () => { const states: DefinedUseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', initialData: 'initial', }) states.push(state) @@ -3115,7 +3209,9 @@ describe('useQuery', () => { const states: DefinedUseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 50, initialData: 'initial', }) @@ -3147,7 +3243,9 @@ describe('useQuery', () => { const oneSecondAgo = Date.now() - 1000 function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 50, initialData: 'initial', initialDataUpdatedAt: oneSecondAgo, @@ -3183,7 +3281,9 @@ describe('useQuery', () => { const states: DefinedUseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'data', { + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 10 * 1000, // 10 seconds initialData: 'initial', initialDataUpdatedAt: 0, @@ -3215,7 +3315,9 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery([key, count], () => ({ count: 10 }), { + const state = useQuery({ + queryKey: [key, count], + queryFn: () => ({ count: 10 }), staleTime: Infinity, initialData: () => ({ count }), }) @@ -3251,9 +3353,10 @@ describe('useQuery', () => { function Page() { const { status, failureCount, failureReason } = useQuery( - key, - queryFn, { + queryKey: key, + queryFn, + retry: 1, retryDelay: 1, }, @@ -3298,7 +3401,9 @@ describe('useQuery', () => { unknown, string, [string] - >(key, queryFn, { + >({ + queryKey: key, + queryFn, retryDelay: 1, retry: (_failureCount, err) => err !== 'NoRetry', }) @@ -3337,7 +3442,9 @@ describe('useQuery', () => { }) function Page() { - const { status, failureCount, failureReason } = useQuery(key, queryFn, { + const { status, failureCount, failureReason } = useQuery({ + queryKey: key, + queryFn, retry: 1, retryDelay: (_, error: DelayError) => error.delay, }) @@ -3373,17 +3480,15 @@ describe('useQuery', () => { let count = 0 function Page() { - const query = useQuery( - key, - () => { + const query = useQuery({ + queryKey: key, + queryFn: () => { count++ return Promise.reject(`fetching error ${count}`) }, - { - retry: 3, - retryDelay: 1, - }, - ) + retry: 3, + retryDelay: 1, + }) return (
    @@ -3436,7 +3541,7 @@ describe('useQuery', () => { queryClient.setQueryData(key, 'prefetched') function Page() { - const state = useQuery(key, () => 'data') + const state = useQuery({ queryKey: key, queryFn: () => 'data' }) states.push(state) return null } @@ -3471,9 +3576,12 @@ describe('useQuery', () => { queryClient.setQueryData(key, 'prefetched') function Page() { - const state = useQuery(key, async () => { - await sleep(10) - return 'data' + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'data' + }, }) states.push(state) return ( @@ -3530,14 +3638,16 @@ describe('useQuery', () => { const prefetchQueryFn = jest.fn() prefetchQueryFn.mockImplementation(() => 'not yet...') - await queryClient.prefetchQuery(key, prefetchQueryFn, { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: prefetchQueryFn, staleTime: 10, }) await sleep(11) function Page() { - const state = useQuery(key, queryFn) + const state = useQuery({ queryKey: key, queryFn }) states.push(state) return null } @@ -3562,16 +3672,16 @@ describe('useQuery', () => { return 'not yet...' }) - await queryClient.prefetchQuery(key, prefetchQueryFn, { + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: prefetchQueryFn, staleTime: 1000, }) await sleep(0) function Page() { - useQuery(key, queryFn, { - staleTime: 1000, - }) + useQuery({ queryKey: key, queryFn, staleTime: 1000 }) return null } @@ -3590,9 +3700,9 @@ describe('useQuery', () => { function Page() { let counter = 0 - const query = useQuery( - key, - async () => { + const query = useQuery({ + queryKey: key, + queryFn: async () => { if (counter < 2) { counter++ throw new Error('error') @@ -3600,8 +3710,8 @@ describe('useQuery', () => { return 'data' } }, - { retryDelay: 10 }, - ) + retryDelay: 10, + }) return (
    @@ -3628,23 +3738,23 @@ describe('useQuery', () => { const [enabled, setEnabled] = React.useState(false) const [isPrefetched, setPrefetched] = React.useState(false) - const query = useQuery( - key, - async () => { + const query = useQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return count }, - { - enabled, - }, - ) + + enabled, + }) React.useEffect(() => { async function prefetch() { - await queryClient.prefetchQuery(key, () => - Promise.resolve('prefetched data'), - ) + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('prefetched data'), + }) act(() => setPrefetched(true)) } prefetch() @@ -3674,7 +3784,9 @@ describe('useQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = React.useState(false) - const query = useQuery(key, () => 'data', { + const query = useQuery({ + queryKey: key, + queryFn: () => 'data', enabled: shouldFetch, }) @@ -3708,7 +3820,11 @@ describe('useQuery', () => { const results: DefinedUseQueryResult[] = [] function Page() { - const result = useQuery(key, () => 'serverData', { initialData: 'data' }) + const result = useQuery({ + queryKey: key, + queryFn: () => 'serverData', + initialData: 'data', + }) results.push(result) return null } @@ -3727,7 +3843,11 @@ describe('useQuery', () => { const results: DefinedUseQueryResult[] = [] function Page() { - const result = useQuery(key, () => 1, { initialData: 0 }) + const result = useQuery({ + queryKey: key, + queryFn: () => 1, + initialData: 0, + }) results.push(result) return null } @@ -3753,21 +3873,19 @@ describe('useQuery', () => { function Page() { const [filter, setFilter] = React.useState('') - const { data: todos } = useQuery( - [...key, filter], - async () => { + const { data: todos } = useQuery({ + queryKey: [...key, filter], + queryFn: async () => { return ALL_TODOS.filter((todo) => filter ? todo.priority === filter : true, ) }, - { - initialData() { - return filter === '' ? initialTodos : undefined - }, - keepPreviousData: true, - staleTime: 5000, + initialData() { + return filter === '' ? initialTodos : undefined }, - ) + keepPreviousData: true, + staleTime: 5000, + }) return (
    @@ -3804,7 +3922,9 @@ describe('useQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = React.useState(true) - const result = useQuery(key, () => 'fetched data', { + const result = useQuery({ + queryKey: key, + queryFn: () => 'fetched data', enabled: shouldFetch, initialData: shouldFetch ? 'initial' : 'initial falsy', }) @@ -3863,7 +3983,7 @@ describe('useQuery', () => { const rendered = renderWithClient(queryClient, ) expect(queryFn).not.toHaveBeenCalled() - expect(queryCache.find(key)).not.toBeUndefined() + expect(queryCache.find({ queryKey: key })).not.toBeUndefined() rendered.getByText('fetchStatus: idle') }) @@ -3872,7 +3992,9 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const query = useQuery(key, () => 'data', { + const query = useQuery({ + queryKey: key, + queryFn: () => 'data', enabled: false, }) @@ -3894,7 +4016,9 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const query = useQuery(key, () => 'fetched data', { + const query = useQuery({ + queryKey: key, + queryFn: () => 'fetched data', cacheTime: Infinity, }) return
    {query.data}
    @@ -3906,7 +4030,7 @@ describe('useQuery', () => { rendered.unmount() - const query = queryCache.find(key) + const query = queryCache.find({ queryKey: key }) // @ts-expect-error expect(query!.cacheTimeout).toBe(undefined) }) @@ -3917,15 +4041,18 @@ describe('useQuery', () => { const memoFn = jest.fn() function Page() { - const result = useQuery(key, async () => { - await sleep(10) - return ( - queryFn() || { - data: { - nested: true, - }, - } - ) + const result = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return ( + queryFn() || { + data: { + nested: true, + }, + } + ) + }, }) React.useMemo(() => { @@ -3959,7 +4086,9 @@ describe('useQuery', () => { function Page() { const [int, setInt] = React.useState(200) - const { data } = useQuery(key, () => count++, { + const { data } = useQuery({ + queryKey: key, + queryFn: () => count++, refetchInterval: int, }) @@ -3986,16 +4115,14 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const queryInfo = useQuery( - key, - async () => { + const queryInfo = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - refetchInterval: (data = 0) => (data < 2 ? 10 : false), - }, - ) + refetchInterval: (data = 0) => (data < 2 ? 10 : false), + }) states.push(queryInfo) @@ -4054,7 +4181,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const queryInfo = useQuery(key, () => 1, { + const queryInfo = useQuery({ + queryKey: key, + queryFn: () => 1, refetchInterval: 0, }) @@ -4087,7 +4216,10 @@ describe('useQuery', () => { it('should accept an empty string as query key', async () => { function Page() { - const result = useQuery([''], (ctx) => ctx.queryKey) + const result = useQuery({ + queryKey: [''], + queryFn: (ctx) => ctx.queryKey, + }) return <>{JSON.stringify(result.data)} } @@ -4098,7 +4230,10 @@ describe('useQuery', () => { it('should accept an object as query key', async () => { function Page() { - const result = useQuery([{ a: 'a' }], (ctx) => ctx.queryKey) + const result = useQuery({ + queryKey: [{ a: 'a' }], + queryFn: (ctx) => ctx.queryKey, + }) return <>{JSON.stringify(result.data)} } @@ -4113,13 +4248,13 @@ describe('useQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Disabled() { - useQuery(key, queryFn, { enabled: false }) + useQuery({ queryKey: key, queryFn, enabled: false }) return null } function Page() { const [enabled, setEnabled] = React.useState(false) - const result = useQuery(key, queryFn, { enabled }) + const result = useQuery({ queryKey: key, queryFn, enabled }) return ( <> @@ -4142,7 +4277,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key1, () => 'data', { + const state = useQuery({ + queryKey: key1, + queryFn: () => 'data', placeholderData: 'placeholder', }) @@ -4181,7 +4318,9 @@ describe('useQuery', () => { function Page() { const [count, setCount] = React.useState(0) - const state = useQuery(key1, () => 'data', { + const state = useQuery({ + queryKey: key1, + queryFn: () => 'data', placeholderData: 'placeholder', enabled: count === 0, }) @@ -4237,7 +4376,9 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key1, () => 1, { + const state = useQuery({ + queryKey: key1, + queryFn: () => 1, placeholderData: 23, select: (data) => String(data * 2), }) @@ -4276,7 +4417,9 @@ describe('useQuery', () => { let placeholderFunctionRunCount = 0 function Page() { - const state = useQuery(key1, () => 1, { + const state = useQuery({ + queryKey: key1, + queryFn: () => 1, placeholderData: () => { placeholderFunctionRunCount++ return 23 @@ -4328,23 +4471,21 @@ describe('useQuery', () => { function Page() { const [count, inc] = React.useReducer((prev) => prev + 1, 2) - const state = useQuery( - key1, - async () => { + const state = useQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 0 }, - { - select: React.useCallback( - (data: number) => { - selectRun++ - return `selected ${data + count}` - }, - [count], - ), - placeholderData: 99, - }, - ) + select: React.useCallback( + (data: number) => { + selectRun++ + return `selected ${data + count}` + }, + [count], + ), + placeholderData: 99, + }) return (
    @@ -4374,22 +4515,21 @@ describe('useQuery', () => { const [count, inc] = React.useReducer((prev) => prev + 1, 2) const [forceValue, forceUpdate] = React.useReducer((prev) => prev + 1, 1) - const state = useQuery( - key1, - async () => { + const state = useQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 0 }, - { - select: React.useCallback( - (data: number) => { - return `selected ${data + count}` - }, - [count], - ), - placeholderData: 99, - }, - ) + + select: React.useCallback( + (data: number) => { + return `selected ${data + count}` + }, + [count], + ), + placeholderData: 99, + }) return (
    @@ -4424,16 +4564,15 @@ describe('useQuery', () => { function Page() { const [forceValue, forceUpdate] = React.useReducer((prev) => prev + 1, 1) - const state = useQuery( - key1, - async () => { + const state = useQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return [1, 2] }, - { - select: (res) => res.map((x) => x + 1), - }, - ) + + select: (res) => res.map((x) => x + 1), + }) React.useEffect(() => { if (state.data) { @@ -4478,7 +4617,7 @@ describe('useQuery', () => { } function Page() { - const state = useQuery(key, queryFn) + const state = useQuery({ queryKey: key, queryFn }) return (

    Status: {state.status}

    @@ -4512,7 +4651,7 @@ describe('useQuery', () => { } function Page(props: { limit: number }) { - const state = useQuery([key, props.limit], queryFn) + const state = useQuery({ queryKey: [key, props.limit], queryFn }) states[props.limit] = state return (
    @@ -4537,25 +4676,25 @@ describe('useQuery', () => { await waitFor(() => expect(states).toHaveLength(4)) - expect(queryCache.find([key, 0])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 0] })?.state).toMatchObject({ data: 'data 0', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find([key, 1])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 1] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', }) - expect(queryCache.find([key, 2])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 2] })?.state).toMatchObject({ data: 'data 2', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find([key, 3])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 3] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', @@ -4575,7 +4714,7 @@ describe('useQuery', () => { const [id, setId] = React.useState(1) const [hasChanged, setHasChanged] = React.useState(false) - const state = useQuery([key, id], queryFn) + const state = useQuery({ queryKey: [key, id], queryFn }) states.push(state) @@ -4619,21 +4758,23 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity }, - ) + staleTime: Infinity, + }) states.push(state) return (
    - +
    data: {state.data ?? 'null'}
    isFetching: {state.isFetching}
    @@ -4687,15 +4828,17 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity, enabled: false, notifyOnChangeProps: 'all' }, - ) + staleTime: Infinity, + enabled: false, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -4704,7 +4847,9 @@ describe('useQuery', () => { return (
    - +
    data: {state.data ?? 'null'}
    ) @@ -4769,7 +4914,7 @@ describe('useQuery', () => { renders++ }) - useQuery(key, () => 'test', { queryKeyHashFn }) + useQuery({ queryKey: key, queryFn: () => 'test', queryKeyHashFn }) return null } @@ -4788,7 +4933,9 @@ describe('useQuery', () => { }) function Page({ enabled }: { enabled: boolean }) { - const { error, isLoading } = useQuery(['key'], queryFn, { + const { error, isLoading } = useQuery({ + queryKey: ['key'], + queryFn, enabled, retry: false, retryOnMount: false, @@ -4839,9 +4986,9 @@ describe('useQuery', () => { it('should refetch when query key changed when previous status is error', async () => { function Page({ id }: { id: number }) { - const { error, isLoading } = useQuery( - [id], - async () => { + const { error, isLoading } = useQuery({ + queryKey: [id], + queryFn: async () => { await sleep(10) if (id % 2 === 1) { return Promise.reject(new Error('Error')) @@ -4849,13 +4996,11 @@ describe('useQuery', () => { return 'data' } }, - { - retry: false, - retryOnMount: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - }, - ) + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }) if (isLoading) { return
    status: loading
    @@ -4898,19 +5043,17 @@ describe('useQuery', () => { it('should refetch when query key changed when switching between erroneous queries', async () => { function Page({ id }: { id: boolean }) { - const { error, isFetching } = useQuery( - [id], - async () => { + const { error, isFetching } = useQuery({ + queryKey: [id], + queryFn: async () => { await sleep(10) return Promise.reject(new Error('Error')) }, - { - retry: false, - retryOnMount: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - }, - ) + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }) if (isFetching) { return
    status: fetching
    @@ -4961,9 +5104,9 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery( - key, - async () => { + const state = useQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (count === 0) { count++ @@ -4971,10 +5114,8 @@ describe('useQuery', () => { } return 5 }, - { - retry: false, - }, - ) + retry: false, + }) states.push(state) @@ -5778,7 +5919,9 @@ describe('useQuery', () => { } function Page() { - const state = useQuery(key, queryFn, { + const state = useQuery({ + queryKey: key, + queryFn, retry: false, retryOnMount: false, }) @@ -5788,7 +5931,7 @@ describe('useQuery', () => { return <> } - await queryClient.prefetchQuery(key, queryFn) + await queryClient.prefetchQuery({ queryKey: key, queryFn }) renderWithClient(queryClient, ) await waitFor(() => expect(states).toHaveLength(1)) @@ -5804,7 +5947,11 @@ describe('useQuery', () => { const onSuccess = jest.fn() function Page() { - const state = useQuery(key, () => 'data', { onSuccess }) + const state = useQuery({ + queryKey: key, + queryFn: () => 'data', + onSuccess, + }) return (
    data: {state.data}
    @@ -5829,14 +5976,16 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const state = useQuery(key, () => 'data') + const state = useQuery({ queryKey: key, queryFn: () => 'data' }) return (
    data: {state.data}
    dataUpdatedAt: {state.dataUpdatedAt}
    diff --git a/packages/react-query/src/__tests__/useQuery.types.test.tsx b/packages/react-query/src/__tests__/useQuery.types.test.tsx index d049b53e00..df8d77adcc 100644 --- a/packages/react-query/src/__tests__/useQuery.types.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.types.test.tsx @@ -15,6 +15,7 @@ describe('initialData', () => { it('TData should always be defined when initialData is provided as an object', () => { doNotExecute(() => { const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -33,6 +34,7 @@ describe('initialData', () => { it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { doNotExecute(() => { const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -51,6 +53,7 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -67,6 +70,7 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { doNotExecute(() => { const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -85,7 +89,8 @@ describe('initialData', () => { describe('Query key overload', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = useQuery(['key'], { + const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -103,7 +108,8 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = useQuery(['key'], { + const { data } = useQuery({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -121,19 +127,17 @@ describe('initialData', () => { describe('Query key and func', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = useQuery( - ['key'], - () => { + const { data } = useQuery({ + queryKey: ['key'], + queryFn: () => { return { wow: true, } }, - { - initialData: { - wow: true, - }, + initialData: { + wow: true, }, - ) + }) const result: Expect> = true return result @@ -142,10 +146,13 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = useQuery(['key'], () => { - return { - wow: true, - } + const { data } = useQuery({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, }) const result: Expect> = diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 3525d3d72c..d030b0a577 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -11,6 +11,7 @@ import type { MutationObserverOptions, MutateFunction, DefinedQueryObserverResult, + WithRequired, } from '@tanstack/query-core' import type { QueryClient } from '@tanstack/query-core' @@ -28,19 +29,19 @@ export interface UseBaseQueryOptions< TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends ContextOptions, - QueryObserverOptions {} + WithRequired< + QueryObserverOptions, + 'queryKey' + > {} export interface UseQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends UseBaseQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey +> extends WithRequired< + UseBaseQueryOptions, + 'queryKey' > {} export interface UseInfiniteQueryOptions< @@ -50,12 +51,15 @@ export interface UseInfiniteQueryOptions< TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends ContextOptions, - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey + WithRequired< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + 'queryKey' > {} export type UseBaseQueryResult< diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index e7c23f5c43..18d574c746 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -1,90 +1,16 @@ -import type { - QueryObserver, - QueryFunction, - QueryKey, -} from '@tanstack/query-core' -import { InfiniteQueryObserver, parseQueryArgs } from '@tanstack/query-core' +import type { QueryObserver, QueryKey } from '@tanstack/query-core' +import { InfiniteQueryObserver } from '@tanstack/query-core' import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' import { useBaseQuery } from './useBaseQuery' // HOOK - -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, -): UseInfiniteQueryResult -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - 'queryKey' - >, -): UseInfiniteQueryResult -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: Omit< - UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - 'queryKey' | 'queryFn' - >, -): UseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | TQueryKey - | UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - arg2?: - | QueryFunction - | UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - arg3?: UseInfiniteQueryOptions< + options: UseInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -92,7 +18,6 @@ export function useInfiniteQuery< TQueryKey >, ): UseInfiniteQueryResult { - const options = parseQueryArgs(arg1, arg2, arg3) return useBaseQuery( options, InfiniteQueryObserver as typeof QueryObserver, diff --git a/packages/react-query/src/useIsFetching.ts b/packages/react-query/src/useIsFetching.ts index e72e5c0ea8..8f80b58ed1 100644 --- a/packages/react-query/src/useIsFetching.ts +++ b/packages/react-query/src/useIsFetching.ts @@ -1,6 +1,6 @@ import * as React from 'react' -import type { QueryKey, QueryFilters } from '@tanstack/query-core' -import { notifyManager, parseFilterArgs } from '@tanstack/query-core' +import type { QueryFilters } from '@tanstack/query-core' +import { notifyManager } from '@tanstack/query-core' import { useSyncExternalStore } from './useSyncExternalStore' import type { ContextOptions } from './types' @@ -8,18 +8,10 @@ import { useQueryClient } from './QueryClientProvider' interface Options extends ContextOptions {} -export function useIsFetching(filters?: QueryFilters, options?: Options): number export function useIsFetching( - queryKey?: QueryKey, filters?: QueryFilters, - options?: Options, -): number -export function useIsFetching( - arg1?: QueryKey | QueryFilters, - arg2?: QueryFilters | Options, - arg3?: Options, + options: Options = {}, ): number { - const [filters, options = {}] = parseFilterArgs(arg1, arg2, arg3) const queryClient = useQueryClient({ context: options.context }) const queryCache = queryClient.getQueryCache() diff --git a/packages/react-query/src/useIsMutating.ts b/packages/react-query/src/useIsMutating.ts index 46c23a274c..7e95b58040 100644 --- a/packages/react-query/src/useIsMutating.ts +++ b/packages/react-query/src/useIsMutating.ts @@ -1,8 +1,8 @@ import * as React from 'react' import { useSyncExternalStore } from './useSyncExternalStore' -import type { MutationKey, MutationFilters } from '@tanstack/query-core' -import { notifyManager, parseMutationFilterArgs } from '@tanstack/query-core' +import type { MutationFilters } from '@tanstack/query-core' +import { notifyManager } from '@tanstack/query-core' import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' @@ -10,20 +10,8 @@ interface Options extends ContextOptions {} export function useIsMutating( filters?: MutationFilters, - options?: Options, -): number -export function useIsMutating( - mutationKey?: MutationKey, - filters?: Omit, - options?: Options, -): number -export function useIsMutating( - arg1?: MutationKey | MutationFilters, - arg2?: Omit | Options, - arg3?: Options, + options: Options = {}, ): number { - const [filters, options = {}] = parseMutationFilterArgs(arg1, arg2, arg3) - const queryClient = useQueryClient({ context: options.context }) const mutationCache = queryClient.getMutationCache() diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index a727fac112..c0f0f82520 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -1,12 +1,6 @@ import * as React from 'react' import { useSyncExternalStore } from './useSyncExternalStore' - -import type { MutationFunction, MutationKey } from '@tanstack/query-core' -import { - notifyManager, - parseMutationArgs, - MutationObserver, -} from '@tanstack/query-core' +import { notifyManager, MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { UseMutateFunction, @@ -24,60 +18,7 @@ export function useMutation< TContext = unknown, >( options: UseMutationOptions, -): UseMutationResult -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationFn: MutationFunction, - options?: Omit< - UseMutationOptions, - 'mutationFn' - >, -): UseMutationResult -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MutationKey, - options?: Omit< - UseMutationOptions, - 'mutationKey' - >, -): UseMutationResult -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MutationKey, - mutationFn?: MutationFunction, - options?: Omit< - UseMutationOptions, - 'mutationKey' | 'mutationFn' - >, -): UseMutationResult -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - arg1: - | MutationKey - | MutationFunction - | UseMutationOptions, - arg2?: - | MutationFunction - | UseMutationOptions, - arg3?: UseMutationOptions, ): UseMutationResult { - const options = parseMutationArgs(arg1, arg2, arg3) const queryClient = useQueryClient({ context: options.context }) const [observer] = React.useState( diff --git a/packages/react-query/src/useQuery.ts b/packages/react-query/src/useQuery.ts index 1ccf7099ed..77904ef528 100644 --- a/packages/react-query/src/useQuery.ts +++ b/packages/react-query/src/useQuery.ts @@ -1,5 +1,5 @@ -import type { QueryFunction, QueryKey } from '@tanstack/query-core' -import { parseQueryArgs, QueryObserver } from '@tanstack/query-core' +import type { QueryKey } from '@tanstack/query-core' +import { QueryObserver } from '@tanstack/query-core' import type { DefinedUseQueryResult, UseQueryOptions, @@ -8,78 +8,23 @@ import type { import { useBaseQuery } from './useBaseQuery' // HOOK - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit< - UseQueryOptions, - 'initialData' - > & { initialData?: () => undefined }, -): UseQueryResult - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit< - UseQueryOptions, - 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, -): DefinedUseQueryResult - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: UseQueryOptions, -): UseQueryResult - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'initialData' - > & { initialData?: () => undefined }, -): UseQueryResult - -export function useQuery< +type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, -): DefinedUseQueryResult +> = UseQueryOptions & { + initialData?: undefined +} -export function useQuery< +type DefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' - >, -): UseQueryResult +> = UseQueryOptions & { + initialData: TQueryFnData | (() => TQueryFnData) +} export function useQuery< TQueryFnData = unknown, @@ -87,12 +32,7 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData?: () => undefined }, + options: UndefinedInitialDataOptions, ): UseQueryResult export function useQuery< @@ -101,12 +41,7 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, + options: DefinedInitialDataOptions, ): DefinedUseQueryResult export function useQuery< @@ -114,27 +49,6 @@ export function useQuery< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' - >, -): UseQueryResult - -export function useQuery< - TQueryFnData, - TError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - arg1: TQueryKey | UseQueryOptions, - arg2?: - | QueryFunction - | UseQueryOptions, - arg3?: UseQueryOptions, -): UseQueryResult { - const parsedOptions = parseQueryArgs(arg1, arg2, arg3) - return useBaseQuery(parsedOptions, QueryObserver) +>(options: UseQueryOptions) { + return useBaseQuery(options, QueryObserver) } diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index 1ac05227c5..5d6fe28280 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -16,9 +16,12 @@ describe('QueryClientProvider', () => { const queryClient = createQueryClient({ queryCache }) function Page() { - const query = createQuery(key, async () => { - await sleep(10) - return 'test' + const query = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -38,7 +41,7 @@ describe('QueryClientProvider', () => { return screen.getByText('test') }) - expect(queryCache.find(key())).toBeDefined() + expect(queryCache.find({ queryKey: key() })).toBeDefined() }) it('allows multiple caches to be partitioned', async () => { @@ -52,9 +55,12 @@ describe('QueryClientProvider', () => { const queryClient2 = createQueryClient({ queryCache: queryCache2 }) function Page1() { - const query = createQuery(key1, async () => { - await sleep(10) - return 'test1' + const query = createQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 'test1' + }, }) return ( @@ -64,9 +70,12 @@ describe('QueryClientProvider', () => { ) } function Page2() { - const query = createQuery(key2, async () => { - await sleep(10) - return 'test2' + const query = createQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 'test2' + }, }) return ( @@ -90,10 +99,10 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test1')) await waitFor(() => screen.getByText('test2')) - expect(queryCache1.find(key1())).toBeDefined() - expect(queryCache1.find(key2())).not.toBeDefined() - expect(queryCache2.find(key1())).not.toBeDefined() - expect(queryCache2.find(key2())).toBeDefined() + expect(queryCache1.find({ queryKey: key1() })).toBeDefined() + expect(queryCache1.find({ queryKey: key2() })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key1() })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key2() })).toBeDefined() }) it("uses defaultOptions for queries when they don't provide their own config", async () => { @@ -110,9 +119,12 @@ describe('QueryClientProvider', () => { }) function Page() { - const query = createQuery(key, async () => { - await sleep(10) - return 'test' + const query = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -130,8 +142,10 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test')) - expect(queryCache.find(key())).toBeDefined() - expect(queryCache.find(key())?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key() })).toBeDefined() + expect(queryCache.find({ queryKey: key() })?.options.cacheTime).toBe( + Infinity, + ) }) describe('with custom context', () => { @@ -153,13 +167,20 @@ describe('QueryClientProvider', () => { }) function Page() { - const queryOuter = createQuery(key, async () => 'testOuter', { + const queryOuter = createQuery({ + queryKey: key, + queryFn: async () => 'testOuter', context: contextOuter, }) - const queryInner = createQuery(key, async () => 'testInner', { + const queryInner = createQuery({ + queryKey: key, + queryFn: async () => 'testInner', context: contextInner, }) - const queryInnerInner = createQuery(key, async () => 'testInnerInner') + const queryInnerInner = createQuery({ + queryKey: key, + queryFn: async () => 'testInnerInner', + }) return (
    diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx index 619cfe2238..1730585e35 100644 --- a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -52,13 +52,11 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - ({ pageParam = 0 }) => Number(pageParam), - { - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => Number(pageParam), + getNextPageParam: (lastPage) => lastPage + 1, + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -147,20 +145,19 @@ describe('useInfiniteQuery', () => { function Page() { const start = 1 - const state = createInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { if (pageParam === 2) { throw new Error('error') } return Number(pageParam) }, - { - retry: 1, - retryDelay: 10, - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + + retry: 1, + retryDelay: 10, + getNextPageParam: (lastPage) => lastPage + 1, + }) createEffect(() => { const fetchNextPage = state.fetchNextPage @@ -192,18 +189,17 @@ describe('useInfiniteQuery', () => { function Page() { const [order, setOrder] = createSignal('desc') - const state = createInfiniteQuery( - () => [key(), order()], - async ({ pageParam = 0 }) => { + const state = createInfiniteQuery({ + queryKey: () => [key(), order()], + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return `${pageParam}-${order()}` }, - { - getNextPageParam: () => 1, - keepPreviousData: true, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: () => 1, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -285,7 +281,9 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery(key, () => ({ count: 1 }), { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: () => ({ count: 1 }), select: (data) => ({ pages: data.pages.map((x) => `count: ${x.count}`), pageParams: data.pageParams, @@ -323,7 +321,9 @@ describe('useInfiniteQuery', () => { let selectCalled = 0 function Page() { - const state = createInfiniteQuery(key, () => ({ count: 1 }), { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: () => ({ count: 1 }), select: (data: InfiniteData<{ count: number }>) => { selectCalled++ return { @@ -363,20 +363,19 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - async ({ pageParam = 0 }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return Number(pageParam) }, - { - select: (data) => ({ - pages: [...data.pages].reverse(), - pageParams: [...data.pageParams].reverse(), - }), - notifyOnChangeProps: 'all', - }, - ) + + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -429,17 +428,16 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(10) return Number(pageParam) }, - { - getPreviousPageParam: (firstPage) => firstPage - 1, - notifyOnChangeProps: 'all', - }, - ) + + getPreviousPageParam: (firstPage) => firstPage - 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -507,9 +505,12 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery(key, async ({ pageParam = 10 }) => { - await sleep(10) - return Number(pageParam) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { + await sleep(10) + return Number(pageParam) + }, }) createRenderEffect(() => { @@ -615,18 +616,17 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - async ({ pageParam = 10 }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { await sleep(10) return Number(pageParam) }, - { - getPreviousPageParam: (firstPage) => firstPage - 1, - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getPreviousPageParam: (firstPage) => firstPage - 1, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -730,17 +730,16 @@ describe('useInfiniteQuery', () => { function Page() { let multiplier = 1 - const state = createInfiniteQuery( - key, - async ({ pageParam = 10 }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 10 }) => { await sleep(10) return Number(pageParam) * multiplier }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -825,17 +824,16 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(50) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -922,7 +920,9 @@ describe('useInfiniteQuery', () => { }) function Page() { - const state = createInfiniteQuery(key, fetchPage, { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, }) @@ -1002,7 +1002,9 @@ describe('useInfiniteQuery', () => { }) function Page() { - const state = createInfiniteQuery(key, fetchPage, { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, }) @@ -1057,17 +1059,16 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery( - key, - async ({ pageParam = start }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = start }) => { await sleep(50) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1115,20 +1116,19 @@ describe('useInfiniteQuery', () => { const initialData = { pages: [1, 2, 3, 4], pageParams: [0, 1, 2, 3] } function List() { - createInfiniteQuery( - key, - async ({ pageParam = 0, signal: _ }) => { + createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0, signal: _ }) => { fetches++ await sleep(50) return Number(pageParam) * 10 }, - { - initialData, - getNextPageParam: (_, allPages) => { - return allPages.length === 4 ? undefined : allPages.length - }, + + initialData, + getNextPageParam: (_, allPages) => { + return allPages.length === 4 ? undefined : allPages.length }, - ) + }) return null } @@ -1166,17 +1166,16 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - async ({ pageParam = 0 }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = 0 }) => { await sleep(10) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1238,17 +1237,16 @@ describe('useInfiniteQuery', () => { function Page() { const [firstPage, setFirstPage] = createSignal(0) - const state = createInfiniteQuery( - key, - async ({ pageParam = firstPage() }) => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam = firstPage() }) => { await sleep(10) return Number(pageParam) }, - { - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1324,18 +1322,17 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - async ({ pageParam }): Promise => { + const state = createInfiniteQuery({ + queryKey: key, + queryFn: async ({ pageParam }): Promise => { await sleep(10) return pageParam }, - { - initialData: { pages: [1], pageParams: [1] }, - getNextPageParam: (lastPage) => lastPage + 1, - notifyOnChangeProps: 'all', - }, - ) + + initialData: { pages: [1], pageParams: [1] }, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1395,13 +1392,12 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - ({ pageParam = 1 }) => Number(pageParam), - { - getNextPageParam: () => undefined, - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 1 }) => Number(pageParam), + + getNextPageParam: () => undefined, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1440,14 +1436,13 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - ({ pageParam = 10 }): number => pageParam, - { - initialData: { pages: [10], pageParams: [undefined] }, - getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }): number => pageParam, + + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), + }) createRenderEffect(() => { states.push({ ...state }) @@ -1485,14 +1480,13 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - ({ pageParam = 10 }): number => pageParam, - { - initialData: { pages: [10], pageParams: [undefined] }, - getNextPageParam: () => undefined, - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 10 }): number => pageParam, + + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: () => undefined, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1530,17 +1524,16 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery( - key, - ({ pageParam = 1 }) => Number(pageParam), - { - getNextPageParam: (lastPage) => (lastPage === 1 ? 2 : false), - select: (data) => ({ - pages: data.pages.map((x) => x.toString()), - pageParams: data.pageParams, - }), - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 1 }) => Number(pageParam), + + getNextPageParam: (lastPage) => (lastPage === 1 ? 2 : false), + select: (data) => ({ + pages: data.pages.map((x) => x.toString()), + pageParams: data.pageParams, + }), + }) createRenderEffect(() => { states.push({ ...state }) @@ -1593,13 +1586,13 @@ describe('useInfiniteQuery', () => { function Page() { let fetchCountRef = 0 - const state = createInfiniteQuery( - key, - ({ pageParam = 0 }) => fetchItemsWithLimit(pageParam, fetchCountRef++), - { - getNextPageParam: (lastPage) => lastPage.nextId, - }, - ) + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => + fetchItemsWithLimit(pageParam, fetchCountRef++), + + getNextPageParam: (lastPage) => lastPage.nextId, + }) return (
    @@ -1643,7 +1636,7 @@ describe('useInfiniteQuery', () => { // makes an actual network request // and calls invalidateQueries in an onSuccess items.splice(4, 1) - queryClient.invalidateQueries(key()) + queryClient.invalidateQueries({ queryKey: key() }) }} > Remove item @@ -1723,18 +1716,17 @@ describe('useInfiniteQuery', () => { let fetchCountRef = 0 const [isRemovedLastPage, setIsRemovedLastPage] = createSignal(false) - const state = createInfiniteQuery( - key, - ({ pageParam = 0 }) => + const state = createInfiniteQuery({ + queryKey: key, + queryFn: ({ pageParam = 0 }) => fetchItems( pageParam, fetchCountRef++, pageParam === MAX || (pageParam === MAX - 1 && isRemovedLastPage()), ), - { - getNextPageParam: (lastPage) => lastPage.nextId, - }, - ) + + getNextPageParam: (lastPage) => lastPage.nextId, + }) return (
    @@ -1862,7 +1854,7 @@ describe('useInfiniteQuery', () => { } function Page() { - const state = createInfiniteQuery(key, queryFn) + const state = createInfiniteQuery({ queryKey: key, queryFn }) return (

    Status: {state.status}

    diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index 68213dfb86..97c4d5725e 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -30,7 +30,9 @@ describe('useMutation', () => { it('should be able to reset `data`', async () => { function Page() { - const mutation = createMutation(() => Promise.resolve('mutation')) + const mutation = createMutation({ + mutationFn: () => Promise.resolve('mutation'), + }) return (
    @@ -64,10 +66,12 @@ describe('useMutation', () => { it('should be able to reset `error`', async () => { function Page() { - const mutation = createMutation(() => { - const err = new Error('Expected mock error. All is well!') - err.stack = '' - return Promise.reject(err) + const mutation = createMutation({ + mutationFn: () => { + const err = new Error('Expected mock error. All is well!') + err.stack = '' + return Promise.reject(err) + }, }) return ( @@ -110,17 +114,15 @@ describe('useMutation', () => { const onSettledMock = jest.fn() function Page() { - const mutation = createMutation( - (vars: { count: number }) => Promise.resolve(vars.count), - { - onSuccess: (data) => { - onSuccessMock(data) - }, - onSettled: (data) => { - onSettledMock(data) - }, + const mutation = createMutation({ + mutationFn: (vars: { count: number }) => Promise.resolve(vars.count), + onSuccess: (data) => { + onSuccessMock(data) }, - ) + onSettled: (data) => { + onSettledMock(data) + }, + }) return (
    @@ -186,7 +188,9 @@ describe('useMutation', () => { }) function Page() { - const mutation = createMutation(mutateFn) + const mutation = createMutation({ + mutationFn: mutateFn, + }) return (
    @@ -234,23 +238,21 @@ describe('useMutation', () => { const [count, setCount] = createSignal(0) function Page() { - const mutation = createMutation( - (vars: { count: number }) => { + const mutation = createMutation({ + mutationFn: (vars: { count: number }) => { const error = new Error( `Expected mock error. All is well! ${vars.count}`, ) error.stack = '' return Promise.reject(error) }, - { - onError: (error: Error) => { - onErrorMock(error.message) - }, - onSettled: (_data, error) => { - onSettledMock(error?.message) - }, + onError: (error: Error) => { + onErrorMock(error.message) }, - ) + onSettled: (_data, error) => { + onSettledMock(error?.message) + }, + }) return (
    @@ -314,7 +316,8 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const mutation = createMutation(async (text: string) => text, { + const mutation = createMutation({ + mutationFn: async (text: string) => text, onSuccess: async () => { callbacks.push('useMutation.onSuccess') }, @@ -364,17 +367,16 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const mutation = createMutation( - async (_text: string) => Promise.reject('oops'), - { - onError: async () => { - callbacks.push('useMutation.onError') - }, - onSettled: async () => { - callbacks.push('useMutation.onSettled') - }, + const mutation = createMutation({ + mutationFn: async (_text: string) => Promise.reject('oops'), + + onError: async () => { + callbacks.push('useMutation.onError') }, - ) + onSettled: async () => { + callbacks.push('useMutation.onSettled') + }, + }) createEffect(() => { const { mutateAsync } = mutation @@ -427,7 +429,9 @@ describe('useMutation', () => { const states: CreateMutationResult[] = [] function Page() { - const mutation = createMutation(key()) + const mutation = createMutation({ + mutationKey: key(), + }) createRenderEffect(() => { states.push({ ...mutation }) @@ -461,16 +465,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation( - (_text: string) => { + const mutation = createMutation({ + mutationFn: (_text: string) => { count++ return Promise.reject('oops') }, - { - retry: 1, - retryDelay: 5, - }, - ) + retry: 1, + retryDelay: 5, + }) createEffect(() => { const { mutate } = mutation @@ -499,16 +501,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation( - (_text: string) => { + const mutation = createMutation({ + mutationFn: (_text: string) => { count++ return Promise.reject(new Error('oops')) }, - { - retry: 1, - retryDelay: 5, - }, - ) + retry: 1, + retryDelay: 5, + }) return (
    @@ -568,16 +568,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - { - onMutate, - }, - ) + onMutate, + }) return (
    @@ -622,10 +620,12 @@ describe('useMutation', () => { const states: Array = [] function Page() { - const mutation = createMutation(async (_text: string) => { - count++ - await sleep(10) - return count + const mutation = createMutation({ + mutationFn: async (_text: string) => { + count++ + await sleep(10) + return count + }, }) createRenderEffect(() => { @@ -674,18 +674,16 @@ describe('useMutation', () => { const states: CreateMutationResult[] = [] function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { await sleep(1) count++ return count > 1 ? Promise.resolve('data') : Promise.reject('oops') }, - { - retry: 1, - retryDelay: 5, - networkMode: 'offlineFirst', - }, - ) + retry: 1, + retryDelay: 5, + networkMode: 'offlineFirst', + }) createRenderEffect(() => { states.push({ ...mutation }) @@ -760,7 +758,7 @@ describe('useMutation', () => { it('should not change state if unmounted', async () => { function Mutates() { - const mutation = createMutation(() => sleep(10)) + const mutation = createMutation({ mutationFn: () => sleep(10) }) return } function Page() { @@ -784,14 +782,14 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is set to true', async () => { function Page() { - const mutation = createMutation( - () => { + const mutation = createMutation({ + mutationFn: () => { const err = new Error('Expected mock error. All is well!') err.stack = '' return Promise.reject(err) }, - { throwErrors: true }, - ) + throwErrors: true, + }) return (
    @@ -824,19 +822,17 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is a function that returns true', async () => { let boundary = false function Page() { - const mutation = createMutation( - () => { + const mutation = createMutation({ + mutationFn: () => { const err = new Error('mock error') err.stack = '' return Promise.reject(err) }, - { - throwErrors: () => { - boundary = !boundary - return !boundary - }, + throwErrors: () => { + boundary = !boundary + return !boundary }, - ) + }) return (
    @@ -892,17 +888,16 @@ describe('useMutation', () => { const metaErrorMessage = 'mutation failed' function Page() { - const mutationSucceed = createMutation(async () => '', { + const mutationSucceed = createMutation({ + mutationFn: async () => '', meta: { metaSuccessMessage }, }) - const mutationError = createMutation( - async () => { + const mutationError = createMutation({ + mutationFn: async () => { throw new Error('') }, - { - meta: { metaErrorMessage }, - }, - ) + meta: { metaErrorMessage }, + }) return (
    @@ -953,19 +948,17 @@ describe('useMutation', () => { } function Component() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - { - mutationKey: mutationKey(), - cacheTime: 0, - onSuccess, - onSettled, - }, - ) + mutationKey: mutationKey(), + cacheTime: 0, + onSuccess, + onSettled, + }) return (
    @@ -1017,7 +1010,8 @@ describe('useMutation', () => { const context = createContext(undefined) function Page() { - const mutation = createMutation(() => Promise.resolve('mutation'), { + const mutation = createMutation({ + mutationFn: () => Promise.resolve('mutation'), context, }) @@ -1055,7 +1049,9 @@ describe('useMutation', () => { const context = createContext(undefined) function Page() { - const { data = '' } = createMutation(() => Promise.resolve('mutation')) + const { data = '' } = createMutation({ + mutationFn: () => Promise.resolve('mutation'), + }) return (
    @@ -1085,17 +1081,15 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { count++ await sleep(10) return `result${count}` }, - { - onSuccess, - onSettled, - }, - ) + onSuccess, + onSettled, + }) return (
    @@ -1149,16 +1143,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { await sleep(10) return 'result' }, - { - onSuccess: () => Promise.reject(error), - onError, - }, - ) + onSuccess: () => Promise.reject(error), + onError, + }) return (
    @@ -1188,15 +1180,13 @@ describe('useMutation', () => { const mutateFnError = new Error('mutateFnError') function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, - { - onError: () => Promise.reject(error), - }, - ) + onError: () => Promise.reject(error), + }) return (
    @@ -1229,16 +1219,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = createMutation( - async (_text: string) => { + const mutation = createMutation({ + mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, - { - onSettled: () => Promise.reject(error), - onError, - }, - ) + onSettled: () => Promise.reject(error), + onError, + }) return (
    diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 7549e1e817..7a093ce266 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -41,51 +41,51 @@ describe('createQuery', () => { // eslint-disable-next-line function Page() { // unspecified query function should default to unknown - const noQueryFn = createQuery(key) + const noQueryFn = createQuery({ queryKey: key }) expectType(noQueryFn.data) expectType(noQueryFn.error) // it should infer the result type from the query function - const fromQueryFn = createQuery(key, () => 'test') + const fromQueryFn = createQuery({ queryKey: key, queryFn: () => 'test' }) expectType(fromQueryFn.data) expectType(fromQueryFn.error) // it should be possible to specify the result type - const withResult = createQuery(key, () => 'test') + const withResult = createQuery({ + queryKey: key, + queryFn: () => 'test', + }) expectType(withResult.data) expectType(withResult.error) // it should be possible to specify the error type - const withError = createQuery(key, () => 'test') + const withError = createQuery({ + queryKey: key, + queryFn: () => 'test', + }) expectType(withError.data) expectType(withError.error) // it should provide the result type in the configuration - createQuery( - () => [key()], - async () => true, - { - onSuccess: (data) => expectType(data), - onSettled: (data) => expectType(data), - }, - ) + createQuery({ + queryKey: () => [key()], + queryFn: async () => true, + onSuccess: (data) => expectType(data), + onSettled: (data) => expectType(data), + }) // it should be possible to specify a union type as result type - const unionTypeSync = createQuery( - key, - () => (Math.random() > 0.5 ? 'a' : 'b'), - { - onSuccess: (data) => expectType<'a' | 'b'>(data), - }, - ) + const unionTypeSync = createQuery({ + queryKey: key, + queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'), + onSuccess: (data) => expectType<'a' | 'b'>(data), + }) expectType<'a' | 'b' | undefined>(unionTypeSync.data) - const unionTypeAsync = createQuery<'a' | 'b'>( - key, - () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), - { - onSuccess: (data) => expectType<'a' | 'b'>(data), - }, - ) + const unionTypeAsync = createQuery<'a' | 'b'>({ + queryKey: key, + queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), + onSuccess: (data) => expectType<'a' | 'b'>(data), + }) expectType<'a' | 'b' | undefined>(unionTypeAsync.data) // should error when the query function result does not match with the specified type @@ -97,7 +97,10 @@ describe('createQuery', () => { return Promise.resolve({} as T) } - const fromGenericQueryFn = createQuery(key, () => queryFn()) + const fromGenericQueryFn = createQuery({ + queryKey: key, + queryFn: () => queryFn(), + }) expectType(fromGenericQueryFn.data) expectType(fromGenericQueryFn.error) @@ -135,9 +138,10 @@ describe('createQuery', () => { }) // it should handle query-functions that return Promise - createQuery(key, () => - fetch('return Promise').then((resp) => resp.json()), - ) + createQuery({ + queryKey: key, + queryFn: () => fetch('return Promise').then((resp) => resp.json()), + }) // handles wrapped queries with custom fetcher passed as inline queryFn const useWrappedQuery = < @@ -156,7 +160,12 @@ describe('createQuery', () => { CreateQueryOptions, 'queryKey' | 'queryFn' | 'initialData' >, - ) => createQuery(qk, () => fetcher(qk()[1], 'token'), options) + ) => + createQuery({ + queryKey: qk, + queryFn: () => fetcher(qk()[1], 'token'), + ...options, + }) const test = useWrappedQuery( () => [''], async () => '1', @@ -176,7 +185,7 @@ describe('createQuery', () => { CreateQueryOptions, 'queryKey' | 'queryFn' | 'initialData' >, - ) => createQuery(qk, fetcher, options) + ) => createQuery({ queryKey: qk, queryFn: fetcher, ...options }) const testFuncStyle = useWrappedFuncStyleQuery( () => [''], async () => true, @@ -190,9 +199,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery(key, async () => { - await sleep(10) - return 'test' + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return ( @@ -218,9 +230,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page(): JSX.Element { - const state = createQuery(key, async () => { - await sleep(10) - return 'test' + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) createRenderEffect(() => { @@ -321,14 +336,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery( - key, - () => Promise.reject('rejected'), - { - retry: 1, - retryDelay: 1, - }, - ) + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject('rejected'), + retry: 1, + retryDelay: 1, + }) createRenderEffect(() => { states.push({ ...state }) @@ -438,10 +451,13 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] // TODO(lukemurray): do we want reactivity on this key? - await queryClient.prefetchQuery(key(), () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: () => 'prefetched', + }) function Page() { - const state = createQuery(key, () => 'data') + const state = createQuery({ queryKey: key, queryFn: () => 'data' }) createRenderEffect(() => { states.push({ ...state }) }) @@ -475,14 +491,14 @@ describe('createQuery', () => { const onSuccess = jest.fn() function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data' }, - { onSuccess }, - ) + onSuccess, + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -508,15 +524,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return 'data' + count }, - { onSuccess }, - ) + onSuccess, + }) createRenderEffect( on( @@ -556,7 +572,9 @@ describe('createQuery', () => { const onSuccess = jest.fn() function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', enabled: false, onSuccess, }) @@ -601,14 +619,14 @@ describe('createQuery', () => { } function Component() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data' }, - { onSuccess }, - ) + onSuccess, + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -632,7 +650,9 @@ describe('createQuery', () => { const onError = jest.fn() function Page() { - const state = createQuery(key, () => Promise.reject('error'), { + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject('error'), retry: false, onError, }) @@ -660,16 +680,14 @@ describe('createQuery', () => { const onError = jest.fn() function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 23 }, - { - onError, - }, - ) + onError, + }) return ( status: {state.status}, fetchStatus: {state.fetchStatus} @@ -684,7 +702,7 @@ describe('createQuery', () => { )) await sleep(5) - await queryClient.cancelQueries(key()) + await queryClient.cancelQueries({ queryKey: key() }) // query cancellation will reset the query to it's initial state await waitFor(() => screen.getByText('status: loading, fetchStatus: idle')) expect(onError).not.toHaveBeenCalled() @@ -696,7 +714,11 @@ describe('createQuery', () => { const onSettled = jest.fn() function Page() { - const state = createQuery(key, () => 'data', { onSettled }) + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', + onSettled, + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -721,7 +743,9 @@ describe('createQuery', () => { const onSettled = jest.fn() function Page() { - const state = createQuery(key, () => Promise.reject('error'), { + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject('error'), retry: false, onSettled, }) @@ -748,15 +772,16 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false, initialData: 'initialData' }, - ) + enabled: false, + initialData: 'initialData', + }) createEffect(() => { setActTimeout(() => { @@ -786,15 +811,16 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false, initialData: 'initialData' }, - ) + enabled: false, + initialData: 'initialData', + }) createEffect(() => { setActTimeout(() => { @@ -824,15 +850,15 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { fetchCount++ await sleep(10) return 'data' }, - { enabled: false }, - ) + enabled: false, + }) createEffect(() => { setActTimeout(() => { @@ -865,7 +891,7 @@ describe('createQuery', () => { queryClient.setQueryDefaults(key(), { queryFn: () => 'data' }) function Page() { - const state = createQuery(key) + const state = createQuery({ queryKey: key }) createRenderEffect(() => { states.push({ ...state }) }) @@ -908,17 +934,15 @@ describe('createQuery', () => { } function Component({ value }: { value: string }) { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'data: ' + value }, - { - cacheTime: 0, - notifyOnChangeProps: 'all', - }, - ) + cacheTime: 0, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -973,7 +997,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'test', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'test', refetchOnMount: false, }) createRenderEffect(() => { @@ -1002,7 +1028,9 @@ describe('createQuery', () => { queryClient.setQueryData(key(), 'prefetched') function Page() { - const state = createQuery(key, () => 'test', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'test', refetchOnMount: false, }) createRenderEffect(() => { @@ -1028,7 +1056,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => ({ name: 'test' }), { + const state = createQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: (data) => data.name, }) createRenderEffect(() => { @@ -1084,7 +1114,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => ({ name: 'test' }), { + const state = createQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: (data) => data.name, notifyOnChangeProps: ['data'], }) @@ -1122,7 +1154,9 @@ describe('createQuery', () => { const error = new Error('Select Error') function Page() { - const state = createQuery(key, () => ({ name: 'test' }), { + const state = createQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), select: () => { throw error }, @@ -1156,16 +1190,15 @@ describe('createQuery', () => { function Page() { //@ts-expect-error -- we skip this test, and no such thing as rerender in solid const [, rerender] = NotReact.useReducer(() => ({}), {}) - const state = createQuery( - key, - () => (runs === 0 ? 'test' : 'test2'), - { - select: () => { - runs++ - throw error - }, + const state = createQuery({ + queryKey: key, + queryFn: () => (runs === 0 ? 'test' : 'test2'), + + select: () => { + runs++ + throw error }, - ) + }) return (
    error: {state.error?.message}
    @@ -1196,9 +1229,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, async () => { - await sleep(10) - return 'test' + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) createRenderEffect(() => { @@ -1241,7 +1277,7 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'test') + const state = createQuery({ queryKey: key, queryFn: () => 'test' }) createRenderEffect(() => { states.push({ ...state }) @@ -1293,15 +1329,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count === 1 ? result1 : result2 }, - { notifyOnChangeProps: 'all' }, - ) + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1354,17 +1390,15 @@ describe('createQuery', () => { queryClient.setQueryData(key(), 'set') function Page() { - const result = createQuery( - key, - async () => { + const result = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'fetched' }, - { - initialData: 'initial', - staleTime: Infinity, - }, - ) + initialData: 'initial', + staleTime: Infinity, + }) createRenderEffect(() => { results.push({ ...result }) @@ -1373,7 +1407,9 @@ describe('createQuery', () => { return (
    isFetching: {result.isFetching}
    - data: {result.data} @@ -1404,15 +1440,16 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity, notifyOnChangeProps: 'all' }, - ) + staleTime: Infinity, + notifyOnChangeProps: 'all', + }) createEffect(() => { states.push({ ...state }) @@ -1420,7 +1457,9 @@ describe('createQuery', () => { return (
    - data: {state.data} @@ -1476,15 +1515,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { enabled: false }, - ) + enabled: false, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1522,15 +1561,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { enabled: false }, - ) + enabled: false, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1538,7 +1577,7 @@ describe('createQuery', () => { createEffect(() => { setActTimeout(() => { - queryClient.invalidateQueries(key()) + queryClient.invalidateQueries({ queryKey: key() }) }, 20) }) @@ -1569,18 +1608,16 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery( - () => [key(), count()], - async () => { + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: async () => { await sleep(5) return count() }, - { - get enabled() { - return count() === 0 - }, + get enabled() { + return count() === 0 }, - ) + }) createRenderEffect(() => { states.push({ ...state }) @@ -1632,14 +1669,14 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery( - () => [key(), count()], - async () => { + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: async () => { await sleep(10) return count() }, - { keepPreviousData: true }, - ) + keepPreviousData: true, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1698,20 +1735,18 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page(props: { count: number }) { - const state = createQuery( - () => [key(), props.count], - async () => { + const state = createQuery({ + queryKey: () => [key(), props.count], + queryFn: async () => { await sleep(10) if (props.count === 2) { throw new Error('Error test') } return Promise.resolve(props.count) }, - { - retry: false, - keepPreviousData: true, - }, - ) + retry: false, + keepPreviousData: true, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1813,14 +1848,15 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery( - () => [key(), count()], - async () => { + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: async () => { await sleep(10) return count() }, - { initialData: 99, keepPreviousData: true }, - ) + initialData: 99, + keepPreviousData: true, + }) createRenderEffect(() => { states.push({ ...state }) @@ -1892,14 +1928,16 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery( - () => [key(), count()], - async () => { + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: async () => { await sleep(10) return count() }, - { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, - ) + enabled: false, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -1986,14 +2024,16 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(10) - const state = createQuery( - () => [key(), count()], - async () => { + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: async () => { await sleep(10) return count() }, - { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, - ) + enabled: false, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -2060,14 +2100,14 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function FirstComponent() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 1 }, - { notifyOnChangeProps: 'all' }, - ) + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -2082,7 +2122,11 @@ describe('createQuery', () => { } function SecondComponent() { - createQuery(key, () => 2, { notifyOnChangeProps: 'all' }) + createQuery({ + queryKey: key, + queryFn: () => 2, + notifyOnChangeProps: 'all', + }) return null } @@ -2126,24 +2170,25 @@ describe('createQuery', () => { const states1: CreateQueryResult[] = [] const states2: CreateQueryResult[] = [] - await queryClient.prefetchQuery(key(), async () => { - await sleep(10) - return 'prefetch' + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: async () => { + await sleep(10) + return 'prefetch' + }, }) await sleep(20) function FirstComponent() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'one' }, - { - staleTime: 100, - }, - ) + staleTime: 100, + }) createRenderEffect(() => { states1.push({ ...state }) }) @@ -2151,16 +2196,14 @@ describe('createQuery', () => { } function SecondComponent() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return 'two' }, - { - staleTime: 10, - }, - ) + staleTime: 10, + }) createRenderEffect(() => { states2.push({ ...state }) }) @@ -2234,7 +2277,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'test', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'test', staleTime: 50, }) createRenderEffect(() => { @@ -2262,16 +2307,15 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(5) return 'test' }, - { - notifyOnChangeProps: ['data'], - }, - ) + + notifyOnChangeProps: ['data'], + }) createRenderEffect(() => { states.push({ ...state }) @@ -2313,12 +2357,16 @@ describe('createQuery', () => { const key2 = queryKey() function Page() { - const first = createQuery(key1, () => 'data', { + const first = createQuery({ + queryKey: key1, + queryFn: () => 'data', enabled: false, initialData: 'init', }) - const second = createQuery(key2, () => 'data', { + const second = createQuery({ + queryKey: key2, + queryFn: () => 'data', enabled: false, initialData: 'init', }) @@ -2359,8 +2407,8 @@ describe('createQuery', () => { } function Page() { - createQuery(key, queryFn1) - createQuery(key, queryFn2) + createQuery({ queryKey: key, queryFn: queryFn1 }) + createQuery({ queryKey: key, queryFn: queryFn2 }) return null } @@ -2370,7 +2418,7 @@ describe('createQuery', () => { )) - expect(queryCache.find(key())!.options.queryFn).toBe(queryFn1) + expect(queryCache.find({ queryKey: key() })!.options.queryFn).toBe(queryFn1) }) it('should batch re-renders', async () => { @@ -2384,8 +2432,8 @@ describe('createQuery', () => { } function Page() { - createQuery(key, queryFn) - createQuery(key, queryFn) + createQuery({ queryKey: key, queryFn }) + createQuery({ queryKey: key, queryFn }) renders++ return null } @@ -2416,12 +2464,16 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - createQuery(key, queryFn, { + createQuery({ + queryKey: key, + queryFn, onSuccess: () => { setCount((x) => x + 1) }, }) - createQuery(key, queryFn, { + createQuery({ + queryKey: key, + queryFn, onSuccess: () => { setCount((x) => x + 1) }, @@ -2454,7 +2506,7 @@ describe('createQuery', () => { function Page() { const [, setNewState] = createSignal('state') - const state = createQuery(key, () => 'data') + const state = createQuery({ queryKey: key, queryFn: () => 'data' }) createEffect(() => { setActTimeout(() => { queryClient.setQueryData(key(), 'new') @@ -2480,10 +2532,12 @@ describe('createQuery', () => { const key2 = queryKey() function Page() { - const first = createQuery(key1, () => 'data', { + const first = createQuery({ + queryKey: key1, + queryFn: () => 'data', enabled: false, }) - const second = createQuery(key2, () => 'data') + const second = createQuery({ queryKey: key2, queryFn: () => 'data' }) return (
    @@ -2515,9 +2569,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const { status } = createQuery(key, async () => { - await sleep(10) - return 'test' + const { status } = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return
    status: {status}
    @@ -2542,13 +2599,13 @@ describe('createQuery', () => { // TODO(lukemurray): extract the query function to a variable queryFn function Page() { - const state = createQuery( - () => [key(), variables] as const, - async (ctx) => { + const state = createQuery({ + queryKey: () => [key(), variables] as const, + queryFn: async (ctx) => { await sleep(10) return ctx.queryKey }, - ) + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -2571,7 +2628,9 @@ describe('createQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Page() { - const { data = 'default' } = createQuery(key, queryFn, { + const { data = 'default' } = createQuery({ + queryKey: key, + queryFn, enabled: false, }) @@ -2601,7 +2660,9 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery(key, () => count++, { + const state = createQuery({ + queryKey: key, + queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: false, }) @@ -2634,7 +2695,9 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery(key, () => count++, { + const state = createQuery({ + queryKey: key, + queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: () => false, }) @@ -2667,7 +2730,9 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery(key, () => count++, { + const state = createQuery({ + queryKey: key, + queryFn: () => count++, staleTime: Infinity, refetchOnWindowFocus: true, }) @@ -2700,17 +2765,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - staleTime: Infinity, - refetchOnWindowFocus: 'always', - }, - ) + staleTime: Infinity, + refetchOnWindowFocus: 'always', + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -2742,18 +2805,16 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - staleTime: 0, - retry: 0, - refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, - }, - ) + staleTime: 0, + retry: 0, + refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -2792,10 +2853,15 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - await queryClient.prefetchQuery(key(), () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: () => 'prefetched', + }) function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: 'always', staleTime: Infinity, }) @@ -2830,12 +2896,17 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - await queryClient.prefetchQuery(key(), () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: () => 'prefetched', + }) await sleep(10) function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: true, staleTime: 0, }) @@ -2870,13 +2941,13 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery( - key, - () => { + const state = createQuery({ + queryKey: key, + queryFn: () => { return Promise.reject('Error test jaylen') }, - { retry: false }, - ) + retry: false, + }) return (
    @@ -2900,11 +2971,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery( - key, - () => Promise.reject('Error test jaylen'), - { retry: false, throwErrors: true }, - ) + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject('Error test jaylen'), + retry: false, + throwErrors: true, + }) return (
    @@ -2931,7 +3003,9 @@ describe('createQuery', () => { let result: CreateQueryResult | undefined function Page() { - const query = createQuery(key, () => Promise.resolve('data'), { + const query = createQuery({ + queryKey: key, + queryFn: () => Promise.resolve('data'), throwErrors: true, }) @@ -2957,14 +3031,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery( - key, - () => Promise.reject('Local Error'), - { - retry: false, - throwErrors: (err) => err !== 'Local Error', - }, - ) + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject('Local Error'), + retry: false, + throwErrors: (err) => err !== 'Local Error', + }) return (
    @@ -2990,14 +3062,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery( - key, - () => Promise.reject(new Error('Remote Error')), - { - retry: false, - throwErrors: (err) => err.message !== 'Local Error', - }, - ) + const state = createQuery({ + queryKey: key, + queryFn: () => Promise.reject(new Error('Remote Error')), + retry: false, + throwErrors: (err) => err.message !== 'Local Error', + }) return (
    @@ -3032,18 +3102,17 @@ describe('createQuery', () => { let count = 0 function Page() { - const result = createQuery( - key, - async () => { + const result = createQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return Promise.reject('some error') }, - { - retry: 2, - retryDelay: 100, - }, - ) + retry: 2, + + retryDelay: 100, + }) return (
    @@ -3088,18 +3157,16 @@ describe('createQuery', () => { let count = 0 function Page() { - const result = createQuery( - key, - async () => { + const result = createQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return Promise.reject('some error') }, - { - retry: 2, - retryDelay: 100, - }, - ) + retry: 2, + retryDelay: 100, + }) return (
    @@ -3150,10 +3217,15 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - await queryClient.prefetchQuery(key(), () => 'prefetched') + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: () => 'prefetched', + }) function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', refetchOnMount: 'always', staleTime: 50, }) @@ -3200,7 +3272,9 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', initialData: 'initial', }) createRenderEffect(() => { @@ -3236,7 +3310,9 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 50, initialData: 'initial', }) @@ -3274,7 +3350,9 @@ describe('createQuery', () => { const oneSecondAgo = Date.now() - 1000 function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 50, initialData: 'initial', initialDataUpdatedAt: oneSecondAgo, @@ -3316,7 +3394,9 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 'data', { + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', staleTime: 10 * 1000, // 10 seconds initialData: 'initial', initialDataUpdatedAt: 0, @@ -3354,14 +3434,12 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery( - () => [key(), count()], - () => ({ count: 10 }), - { - staleTime: Infinity, - initialData: () => ({ count: count() }), - }, - ) + const state = createQuery({ + queryKey: () => [key(), count()], + queryFn: () => ({ count: 10 }), + staleTime: Infinity, + initialData: () => ({ count: count() }), + }) createRenderEffect(() => { states.push({ ...state }) }) @@ -3399,7 +3477,9 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery(key, queryFn, { + const state = createQuery({ + queryKey: key, + queryFn, retry: 1, retryDelay: 1, }) @@ -3443,7 +3523,9 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery(key, queryFn, { + const state = createQuery({ + queryKey: key, + queryFn, retryDelay: 1, retry: (_failureCount, err) => err !== 'NoRetry', }) @@ -3484,7 +3566,9 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery(key, queryFn, { + const state = createQuery({ + queryKey: key, + queryFn, retry: 1, retryDelay: (_, error: DelayError) => error.delay, }) @@ -3524,17 +3608,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const query = createQuery( - key, - () => { + const query = createQuery({ + queryKey: key, + queryFn: () => { count++ return Promise.reject(`fetching error ${count}`) }, - { - retry: 3, - retryDelay: 1, - }, - ) + retry: 3, + retryDelay: 1, + }) return (
    @@ -3588,7 +3670,7 @@ describe('createQuery', () => { queryClient.setQueryData(key(), 'prefetched') function Page() { - const state = createQuery(key, () => 'data') + const state = createQuery({ queryKey: key, queryFn: () => 'data' }) createRenderEffect(() => { states.push({ ...state }) }) @@ -3629,9 +3711,12 @@ describe('createQuery', () => { queryClient.setQueryData(key(), 'prefetched') function Page() { - const state = createQuery(key, async () => { - await sleep(10) - return 'data' + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'data' + }, }) createRenderEffect(() => { states.push({ ...state }) @@ -3692,14 +3777,16 @@ describe('createQuery', () => { const prefetchQueryFn = jest.fn() prefetchQueryFn.mockImplementation(() => 'not yet...') - await queryClient.prefetchQuery(key(), prefetchQueryFn, { + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: prefetchQueryFn, staleTime: 10, }) await sleep(11) function Page() { - const state = createQuery(key, queryFn) + const state = createQuery({ queryKey: key, queryFn }) createRenderEffect(() => { states.push({ ...state }) }) @@ -3730,16 +3817,16 @@ describe('createQuery', () => { return 'not yet...' }) - await queryClient.prefetchQuery(key(), prefetchQueryFn, { + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: prefetchQueryFn, staleTime: 1000, }) await sleep(0) function Page() { - createQuery(key, queryFn, { - staleTime: 1000, - }) + createQuery({ queryKey: key, queryFn, staleTime: 1000 }) return null } @@ -3762,9 +3849,9 @@ describe('createQuery', () => { function Page() { let counter = 0 - const query = createQuery( - key, - async () => { + const query = createQuery({ + queryKey: key, + queryFn: async () => { if (counter < 2) { counter++ throw new Error('error') @@ -3772,8 +3859,8 @@ describe('createQuery', () => { return 'data' } }, - { retryDelay: 10 }, - ) + retryDelay: 10, + }) return (
    @@ -3804,25 +3891,24 @@ describe('createQuery', () => { const [enabled, setEnabled] = createSignal(false) const [isPrefetched, setPrefetched] = createSignal(false) - const query = createQuery( - key, - async () => { + const query = createQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return count }, - { - get enabled() { - return enabled() - }, + get enabled() { + return enabled() }, - ) + }) createEffect(() => { async function prefetch() { - await queryClient.prefetchQuery(key(), () => - Promise.resolve('prefetched data'), - ) + await queryClient.prefetchQuery({ + queryKey: key(), + queryFn: () => Promise.resolve('prefetched data'), + }) setPrefetched(true) } prefetch() @@ -3856,7 +3942,9 @@ describe('createQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = createSignal(false) - const query = createQuery(key, () => 'data', { + const query = createQuery({ + queryKey: key, + queryFn: () => 'data', get enabled() { return shouldFetch() }, @@ -3896,7 +3984,9 @@ describe('createQuery', () => { const results: DefinedCreateQueryResult[] = [] function Page() { - const result = createQuery(key, () => 'serverData', { + const result = createQuery({ + queryKey: key, + queryFn: () => 'serverData', initialData: 'data', }) @@ -3925,7 +4015,11 @@ describe('createQuery', () => { const results: DefinedCreateQueryResult[] = [] function Page() { - const result = createQuery(key, () => 1, { initialData: 0 }) + const result = createQuery({ + queryKey: key, + queryFn: () => 1, + initialData: 0, + }) createRenderEffect(() => { results.push({ ...result }) @@ -3955,7 +4049,9 @@ describe('createQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = createSignal(true) - const result = createQuery(key, () => 'fetched data', { + const result = createQuery({ + queryKey: key, + queryFn: () => 'fetched data', get enabled() { return shouldFetch() }, @@ -4011,7 +4107,7 @@ describe('createQuery', () => { )) expect(queryFn).not.toHaveBeenCalled() - expect(queryCache.find(key())).not.toBeUndefined() + expect(queryCache.find({ queryKey: key() })).not.toBeUndefined() screen.getByText('fetchStatus: idle') }) @@ -4020,7 +4116,9 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const query = createQuery(key, () => 'data', { + const query = createQuery({ + queryKey: key, + queryFn: () => 'data', enabled: false, }) @@ -4046,7 +4144,9 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const query = createQuery(key, () => 'fetched data', { + const query = createQuery({ + queryKey: key, + queryFn: () => 'fetched data', cacheTime: Infinity, }) return
    {query.data}
    @@ -4062,7 +4162,7 @@ describe('createQuery', () => { result.unmount() - const query = queryCache.find(key()) + const query = queryCache.find({ queryKey: key() }) // @ts-expect-error expect(query!.cacheTimeout).toBe(undefined) }) @@ -4073,15 +4173,18 @@ describe('createQuery', () => { const memoFn = jest.fn() function Page() { - const result = createQuery(key, async () => { - await sleep(10) - return ( - queryFn() || { - data: { - nested: true, - }, - } - ) + const result = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return ( + queryFn() || { + data: { + nested: true, + }, + } + ) + }, }) createMemo(() => { @@ -4119,7 +4222,9 @@ describe('createQuery', () => { function Page() { const [int, setInt] = createSignal(200) - const state = createQuery(key, () => count++, { + const state = createQuery({ + queryKey: key, + queryFn: () => count++, get refetchInterval() { return int() }, @@ -4152,16 +4257,14 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) return count++ }, - { - refetchInterval: (data = 0) => (data < 2 ? 10 : false), - }, - ) + refetchInterval: (data = 0) => (data < 2 ? 10 : false), + }) createRenderEffect(() => { states.push({ ...state }) @@ -4226,7 +4329,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key, () => 1, { + const state = createQuery({ + queryKey: key, + queryFn: () => 1, refetchInterval: 0, }) @@ -4265,10 +4370,10 @@ describe('createQuery', () => { it('should accept an empty string as query key', async () => { function Page() { - const result = createQuery( - () => [''], - (ctx) => ctx.queryKey, - ) + const result = createQuery({ + queryKey: () => [''], + queryFn: (ctx) => ctx.queryKey, + }) return <>{JSON.stringify(result.data)} } @@ -4283,10 +4388,10 @@ describe('createQuery', () => { it('should accept an object as query key', async () => { function Page() { - const result = createQuery( - () => [{ a: 'a' }], - (ctx) => ctx.queryKey, - ) + const result = createQuery({ + queryKey: () => [{ a: 'a' }], + queryFn: (ctx) => ctx.queryKey, + }) return <>{JSON.stringify(result.data)} } @@ -4305,13 +4410,15 @@ describe('createQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Disabled() { - createQuery(key, queryFn, { enabled: false }) + createQuery({ queryKey: key, queryFn, enabled: false }) return null } function Page() { const [enabled, setEnabled] = createSignal(false) - const result = createQuery(key, queryFn, { + const result = createQuery({ + queryKey: key, + queryFn, get enabled() { return enabled() }, @@ -4342,7 +4449,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key1, () => 'data', { + const state = createQuery({ + queryKey: key1, + queryFn: () => 'data', placeholderData: 'placeholder', }) @@ -4387,7 +4496,9 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery(key1, () => 'data', { + const state = createQuery({ + queryKey: key1, + queryFn: () => 'data', placeholderData: 'placeholder', get enabled() { return count() === 0 @@ -4451,7 +4562,9 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery(key1, () => 1, { + const state = createQuery({ + queryKey: key1, + queryFn: () => 1, placeholderData: 23, select: (data) => String(data * 2), }) @@ -4496,7 +4609,9 @@ describe('createQuery', () => { let placeholderFunctionRunCount = 0 function Page() { - const state = createQuery(key1, () => 1, { + const state = createQuery({ + queryKey: key1, + queryFn: () => 1, placeholderData: () => { placeholderFunctionRunCount++ return 23 @@ -4549,24 +4664,22 @@ describe('createQuery', () => { //@ts-expect-error skip this test const [count, inc] = NotReact.useReducer((prev) => prev + 1, 2) - const state = createQuery( - key1, - async () => { + const state = createQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 0 }, - { - //@ts-expect-error skip this test - select: NotReact.useCallback( - (data: number) => { - selectRun++ - return `selected ${data + count}` - }, - [count], - ), - placeholderData: 99, - }, - ) + //@ts-expect-error skip this test + select: NotReact.useCallback( + (data: number) => { + selectRun++ + return `selected ${data + count}` + }, + [count], + ), + placeholderData: 99, + }) return (
    @@ -4608,20 +4721,19 @@ describe('createQuery', () => { setForceValue((prev) => prev + 1) } - const state = createQuery( - key1, - async () => { + const state = createQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return 0 }, - { - get select() { - const currentCount = count() - return (data: number) => `selected ${data + currentCount}` - }, - placeholderData: 99, + + get select() { + const currentCount = count() + return (data: number) => `selected ${data + currentCount}` }, - ) + placeholderData: 99, + }) return (
    @@ -4660,16 +4772,14 @@ describe('createQuery', () => { function Page() { const [forceValue, setForceValue] = createSignal(1) - const state = createQuery( - key1, - async () => { + const state = createQuery({ + queryKey: key1, + queryFn: async () => { await sleep(10) return [1, 2] }, - { - select: (res) => res.map((x) => x + 1), - }, - ) + select: (res) => res.map((x) => x + 1), + }) createEffect(() => { if (state.data) { @@ -4722,7 +4832,7 @@ describe('createQuery', () => { } function Page() { - const state = createQuery(key, queryFn) + const state = createQuery({ queryKey: key, queryFn }) return (

    Status: {state.status}

    @@ -4758,7 +4868,10 @@ describe('createQuery', () => { } function Page(props: { limit: number }) { - const state = createQuery(() => [key(), props.limit] as const, queryFn) + const state = createQuery({ + queryKey: () => [key(), props.limit] as const, + queryFn, + }) states[props.limit] = state return (
    @@ -4784,25 +4897,25 @@ describe('createQuery', () => { await waitFor(() => expect(states).toHaveLength(4)) - expect(queryCache.find([key(), 0])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key(), 0] })?.state).toMatchObject({ data: 'data 0', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find([key(), 1])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key(), 1] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', }) - expect(queryCache.find([key(), 2])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key(), 2] })?.state).toMatchObject({ data: 'data 2', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find([key(), 3])?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key(), 3] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', @@ -4822,7 +4935,7 @@ describe('createQuery', () => { const [id, setId] = createSignal(1) const [hasChanged, setHasChanged] = createSignal(false) - const state = createQuery(() => [key(), id()], queryFn) + const state = createQuery({ queryKey: () => [key(), id()], queryFn }) createRenderEffect(() => { states.push({ ...state }) @@ -4865,15 +4978,15 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity }, - ) + staleTime: Infinity, + }) createRenderEffect(() => { states.push({ ...state }) @@ -4881,7 +4994,9 @@ describe('createQuery', () => { return (
    - +
    data: {state.data ?? 'null'}
    isFetching: {state.isFetching}
    @@ -4939,15 +5054,17 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) count++ return count }, - { staleTime: Infinity, enabled: false, notifyOnChangeProps: 'all' }, - ) + staleTime: Infinity, + enabled: false, + notifyOnChangeProps: 'all', + }) createRenderEffect(() => { states.push({ ...state }) @@ -4958,7 +5075,9 @@ describe('createQuery', () => { return (
    - +
    data: {state.data ?? 'null'}
    ) @@ -5023,7 +5142,11 @@ describe('createQuery', () => { } function Page() { - const state = createQuery(key, () => 'test', { queryKeyHashFn }) + const state = createQuery({ + queryKey: key, + queryFn: () => 'test', + queryKeyHashFn, + }) createEffect( on( () => state.status, @@ -5054,7 +5177,9 @@ describe('createQuery', () => { }) function Page(props: { enabled: boolean }) { - const state = createQuery(() => ['key'], queryFn, { + const state = createQuery({ + queryKey: () => ['key'], + queryFn, get enabled() { return props.enabled }, @@ -5115,9 +5240,9 @@ describe('createQuery', () => { it('should refetch when query key changed when previous status is error', async () => { function Page(props: { id: number }) { - const state = createQuery( - () => [props.id], - async () => { + const state = createQuery({ + queryKey: () => [props.id], + queryFn: async () => { await sleep(10) if (props.id % 2 === 1) { return Promise.reject(new Error('Error')) @@ -5125,13 +5250,11 @@ describe('createQuery', () => { return 'data' } }, - { - retry: false, - retryOnMount: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - }, - ) + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }) return ( rendered
    }> @@ -5182,19 +5305,17 @@ describe('createQuery', () => { it('should refetch when query key changed when switching between erroneous queries', async () => { function Page(props: { id: boolean }) { - const state = createQuery( - () => [props.id], - async () => { + const state = createQuery({ + queryKey: () => [props.id], + queryFn: async () => { await sleep(10) return Promise.reject(new Error('Error')) }, - { - retry: false, - retryOnMount: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - }, - ) + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }) return ( rendered
    }> @@ -5252,9 +5373,9 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (count === 0) { count++ @@ -5262,10 +5383,8 @@ describe('createQuery', () => { } return 5 }, - { - retry: false, - }, - ) + retry: false, + }) createRenderEffect(() => { states.push({ ...state }) @@ -6116,7 +6235,9 @@ describe('createQuery', () => { } function Page() { - const state = createQuery(key, queryFn, { + const state = createQuery({ + queryKey: key, + queryFn, retry: false, retryOnMount: false, }) @@ -6128,7 +6249,7 @@ describe('createQuery', () => { return <> } - await queryClient.prefetchQuery(key(), queryFn) + await queryClient.prefetchQuery({ queryKey: key(), queryFn }) render(() => ( @@ -6148,7 +6269,11 @@ describe('createQuery', () => { const onSuccess = jest.fn() function Page() { - const state = createQuery(key, () => 'data', { onSuccess }) + const state = createQuery({ + queryKey: key, + queryFn: () => 'data', + onSuccess, + }) return (
    data: {state.data}
    @@ -6177,14 +6302,16 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery(key, () => 'data') + const state = createQuery({ queryKey: key, queryFn: () => 'data' }) return (
    data: {state.data}
    dataUpdatedAt: {state.dataUpdatedAt}
    diff --git a/packages/solid-query/src/__tests__/createQuery.types.test.tsx b/packages/solid-query/src/__tests__/createQuery.types.test.tsx index 1d82484d5b..eda398c8bc 100644 --- a/packages/solid-query/src/__tests__/createQuery.types.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.types.test.tsx @@ -15,6 +15,7 @@ describe('initialData', () => { it('TData should always be defined when initialData is provided as an object', () => { doNotExecute(() => { const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -33,6 +34,7 @@ describe('initialData', () => { it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { doNotExecute(() => { const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -51,6 +53,7 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -67,6 +70,7 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { doNotExecute(() => { const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -85,7 +89,8 @@ describe('initialData', () => { describe('Query key overload', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = createQuery(() => ['key'], { + const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -103,7 +108,8 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = createQuery(() => ['key'], { + const { data } = createQuery({ + queryKey: () => ['key'], queryFn: () => { return { wow: true, @@ -121,19 +127,18 @@ describe('initialData', () => { describe('Query key and func', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = createQuery( - () => ['key'], - () => { + const { data } = createQuery({ + queryKey: () => ['key'], + queryFn: () => { return { wow: true, } }, - { - initialData: { - wow: true, - }, + + initialData: { + wow: true, }, - ) + }) const result: Expect> = true return result @@ -142,14 +147,14 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = createQuery( - () => ['key'], - () => { + const { data } = createQuery({ + queryKey: () => ['key'], + queryFn: () => { return { wow: true, } }, - ) + }) const result: Expect> = true diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index 5da5cf1501..80a57250c2 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -31,15 +31,15 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [stateKey, setStateKey] = createSignal(key()) - const state = createQuery( - stateKey, - async () => { + const state = createQuery({ + queryKey: stateKey, + queryFn: async () => { count++ await sleep(10) return count }, - { suspense: true }, - ) + suspense: true, + }) createRenderEffect(() => { states.push({ ...state }) @@ -87,17 +87,16 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [multiplier, setMultiplier] = createSignal(1) - const state = createInfiniteQuery( - () => [`${key()}_${multiplier()}`], - async ({ pageParam = 1 }) => { + const state = createInfiniteQuery({ + queryKey: () => [`${key()}_${multiplier()}`], + queryFn: async ({ pageParam = 1 }) => { await sleep(10) return Number(pageParam * multiplier()) }, - { - suspense: true, - getNextPageParam: (lastPage) => lastPage + 1, - }, - ) + + suspense: true, + getNextPageParam: (lastPage) => lastPage + 1, + }) createRenderEffect(() => { states.push({ ...state }) @@ -150,7 +149,7 @@ describe("useQuery's in Suspense mode", () => { }) function Page() { - createQuery(() => [key()], queryFn, { suspense: true }) + createQuery({ queryKey: () => [key()], queryFn, suspense: true }) return <>rendered } @@ -172,14 +171,14 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - createQuery( - key, - () => { + createQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { suspense: true }, - ) + suspense: true, + }) return <>rendered } @@ -205,17 +204,17 @@ describe("useQuery's in Suspense mode", () => { )) expect(screen.queryByText('rendered')).toBeNull() - expect(queryCache.find(key())).toBeFalsy() + expect(queryCache.find({ queryKey: key() })).toBeFalsy() fireEvent.click(screen.getByLabelText('toggle')) await waitFor(() => screen.getByText('rendered')) - expect(queryCache.find(key())?.getObserversCount()).toBe(1) + expect(queryCache.find({ queryKey: key() })?.getObserversCount()).toBe(1) fireEvent.click(screen.getByLabelText('toggle')) expect(screen.queryByText('rendered')).toBeNull() - expect(queryCache.find(key())?.getObserversCount()).toBe(0) + expect(queryCache.find({ queryKey: key() })?.getObserversCount()).toBe(0) }) it('should call onSuccess on the first successful call', async () => { @@ -224,18 +223,17 @@ describe("useQuery's in Suspense mode", () => { const successFn = jest.fn() function Page() { - createQuery( - () => [key()], - async () => { + createQuery({ + queryKey: () => [key()], + queryFn: async () => { await sleep(10) return key() }, - { - suspense: true, - select: () => 'selected', - onSuccess: successFn, - }, - ) + + suspense: true, + select: () => 'selected', + onSuccess: successFn, + }) return <>rendered } @@ -261,33 +259,31 @@ describe("useQuery's in Suspense mode", () => { const successFn2 = jest.fn() function FirstComponent() { - createQuery( - key, - () => { + createQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { - suspense: true, - onSuccess: successFn1, - }, - ) + + suspense: true, + onSuccess: successFn1, + }) return first } function SecondComponent() { - createQuery( - key, - () => { + createQuery({ + queryKey: key, + queryFn: () => { sleep(10) return 'data' }, - { - suspense: true, - onSuccess: successFn2, - }, - ) + + suspense: true, + onSuccess: successFn2, + }) return second } @@ -315,9 +311,9 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { @@ -326,11 +322,10 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retryDelay: 10, - suspense: true, - }, - ) + + retryDelay: 10, + suspense: true, + }) // Suspense only triggers if used in JSX return ( @@ -381,9 +376,9 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - const state = createQuery( - key, - async () => { + const state = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -391,11 +386,10 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) // Suspense only triggers if used in JSX return ( @@ -444,19 +438,18 @@ describe("useQuery's in Suspense mode", () => { let count = 0 function Component() { - const result = createQuery( - key, - async () => { + const result = createQuery({ + queryKey: key, + queryFn: async () => { await sleep(100) count++ return count }, - { - retry: false, - suspense: true, - staleTime: 0, - }, - ) + + retry: false, + suspense: true, + staleTime: 0, + }) return (
    data: {result.data} @@ -504,17 +497,16 @@ describe("useQuery's in Suspense mode", () => { const key2 = queryKey() function Component(props: { queryKey: Array }) { - const result = createQuery( - () => props.queryKey, - async () => { + const result = createQuery({ + queryKey: () => props.queryKey, + queryFn: async () => { await sleep(100) return props.queryKey }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) return
    data: {result.data}
    } @@ -549,7 +541,8 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText(`data: ${key2()}`)) expect( // @ts-expect-error - queryClient.getQueryCache().find(key2())!.observers[0].listeners.length, + queryClient.getQueryCache().find({ queryKey: key2() })!.observers[0] + .listeners.length, ).toBe(1) }) @@ -557,17 +550,16 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - const state = createQuery( - key, - async (): Promise => { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) throw new Error('Suspense Error a1x') }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) // read state.data to trigger suspense. createRenderEffect(() => { @@ -608,18 +600,17 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - const state = createQuery( - key, - async (): Promise => { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) throw new Error('Suspense Error a2x') }, - { - retry: false, - suspense: true, - throwErrors: false, - }, - ) + + retry: false, + suspense: true, + throwErrors: false, + }) // read state.data to trigger suspense. createRenderEffect(() => { @@ -660,18 +651,17 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - const state = createQuery( - key, - async (): Promise => { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) return Promise.reject('Remote Error') }, - { - retry: false, - suspense: true, - throwErrors: (err) => err !== 'Local Error', - }, - ) + + retry: false, + suspense: true, + throwErrors: (err) => err !== 'Local Error', + }) // read state.data to trigger suspense. createRenderEffect(() => { @@ -712,18 +702,17 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - const state = createQuery( - key, - async (): Promise => { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { await sleep(10) return Promise.reject('Local Error') }, - { - retry: false, - suspense: true, - throwErrors: (err) => err !== 'Local Error', - }, - ) + + retry: false, + suspense: true, + throwErrors: (err) => err !== 'Local Error', + }) // read state.data to trigger suspense. createRenderEffect(() => { @@ -771,7 +760,9 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [enabled, setEnabled] = createSignal(false) - const result = createQuery(() => [key()], queryFn, { + const result = createQuery({ + queryKey: () => [key()], + queryFn, suspense: true, get enabled() { return enabled() @@ -813,9 +804,9 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [nonce] = createSignal(0) const queryKeys = () => [`${key()}-${succeed}`] - const result = createQuery( - queryKeys, - async () => { + const result = createQuery({ + queryKey: queryKeys, + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -823,11 +814,10 @@ describe("useQuery's in Suspense mode", () => { return nonce() } }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) return (
    rendered {result.data} @@ -878,9 +868,9 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [key, setKey] = createSignal(0) - const result = createQuery( - () => [`${key()}-${succeed}`], - async () => { + const result = createQuery({ + queryKey: () => [`${key()}-${succeed}`], + queryFn: async () => { await sleep(10) if (!succeed) { throw new Error('Suspense Error Bingo') @@ -888,11 +878,10 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - { - retry: false, - suspense: true, - }, - ) + + retry: false, + suspense: true, + }) return (
    rendered {result.data} @@ -937,20 +926,19 @@ describe("useQuery's in Suspense mode", () => { const queryKeys = '1' const [enabled, setEnabled] = createSignal(false) - const result = createQuery( - () => [queryKeys], - async () => { + const result = createQuery({ + queryKey: () => [queryKeys], + queryFn: async () => { await sleep(10) throw new Error('Suspense Error Bingo') }, - { - retry: false, - suspense: true, - get enabled() { - return enabled() - }, + + retry: false, + suspense: true, + get enabled() { + return enabled() }, - ) + }) return (
    rendered {result.data} @@ -1003,15 +991,16 @@ describe("useQuery's in Suspense mode", () => { let renders = 0 function Page() { - state = createQuery( - key, - async () => { + state = createQuery({ + queryKey: key, + queryFn: async () => { count++ await sleep(10) return count }, - { suspense: true, cacheTime: 0 }, - ) + suspense: true, + cacheTime: 0, + }) createRenderEffect( on([() => ({ ...state })], () => { diff --git a/packages/solid-query/src/__tests__/transition.test.tsx b/packages/solid-query/src/__tests__/transition.test.tsx index af26437cef..730e51266a 100644 --- a/packages/solid-query/src/__tests__/transition.test.tsx +++ b/packages/solid-query/src/__tests__/transition.test.tsx @@ -12,9 +12,12 @@ describe("useQuery's in Suspense mode with transitions", () => { const key = queryKey() function Suspended() { - const state = createQuery(key, async () => { - await sleep(10) - return true + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return true + }, }) return Message diff --git a/packages/solid-query/src/__tests__/useIsFetching.test.tsx b/packages/solid-query/src/__tests__/useIsFetching.test.tsx index ad1a48fb66..bac846ab00 100644 --- a/packages/solid-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/solid-query/src/__tests__/useIsFetching.test.tsx @@ -27,18 +27,17 @@ describe('useIsFetching', () => { function Query() { const [ready, setReady] = createSignal(false) - createQuery( - key, - async () => { + createQuery({ + queryKey: key, + queryFn: async () => { await sleep(50) return 'test' }, - { - get enabled() { - return ready() - }, + + get enabled() { + return ready() }, - ) + }) return } @@ -82,17 +81,23 @@ describe('useIsFetching', () => { } function FirstQuery() { - createQuery(key1, async () => { - await sleep(100) - return 'data' + createQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(100) + return 'data' + }, }) return null } function SecondQuery() { - createQuery(key2, async () => { - await sleep(100) - return 'data' + createQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(100) + return 'data' + }, }) return null } @@ -134,24 +139,30 @@ describe('useIsFetching', () => { const isFetchings: number[] = [] function One() { - createQuery(key1, async () => { - await sleep(10) - return 'test' + createQuery({ + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) return null } function Two() { - createQuery(key2, async () => { - await sleep(20) - return 'test' + createQuery({ + queryKey: key2, + queryFn: async () => { + await sleep(20) + return 'test' + }, }) return null } function Page() { const [started, setStarted] = createSignal(false) - const isFetching = useIsFetching(key1) + const isFetching = useIsFetching({ queryKey: key1 }) createRenderEffect(() => { isFetchings.push(isFetching()) @@ -198,19 +209,18 @@ describe('useIsFetching', () => { const isFetching = useIsFetching(undefined, { context: context }) - createQuery( - key, - async () => { + createQuery({ + queryKey: key, + queryFn: async () => { await sleep(50) return 'test' }, - { - get enabled() { - return ready() - }, - context, + + get enabled() { + return ready() }, - ) + context, + }) return (
    @@ -242,7 +252,9 @@ describe('useIsFetching', () => { function Page() { const isFetching = useIsFetching() - createQuery(key, async () => 'test', { + createQuery({ + queryKey: key, + queryFn: async () => 'test', enabled: true, context, throwErrors: true, @@ -272,9 +284,12 @@ describe('useIsFetching', () => { const key = queryKey() function Page() { - createQuery(key, async () => { - await sleep(10) - return 'test' + createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, }) const isFetching = useIsFetching() diff --git a/packages/solid-query/src/__tests__/useIsMutating.test.tsx b/packages/solid-query/src/__tests__/useIsMutating.test.tsx index faa285ee22..eff08bc7d2 100644 --- a/packages/solid-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/solid-query/src/__tests__/useIsMutating.test.tsx @@ -33,13 +33,19 @@ describe('useIsMutating', () => { } function Mutations() { - const { mutate: mutate1 } = createMutation(['mutation1'], async () => { - await sleep(150) - return 'data' + const { mutate: mutate1 } = createMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(150) + return 'data' + }, }) - const { mutate: mutate2 } = createMutation(['mutation2'], async () => { - await sleep(50) - return 'data' + const { mutate: mutate2 } = createMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(50) + return 'data' + }, }) createEffect(() => { @@ -74,7 +80,7 @@ describe('useIsMutating', () => { const queryClient = createQueryClient() function IsMutating() { - const isMutating = useIsMutating(['mutation1']) + const isMutating = useIsMutating({ mutationKey: ['mutation1'] }) createRenderEffect(() => { isMutatings.push(isMutating()) }) @@ -82,13 +88,19 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation(['mutation1'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate1 } = createMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) - const { mutate: mutate2 } = createMutation(['mutation2'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate2 } = createMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) createEffect(() => { @@ -124,13 +136,19 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation(['mutation1'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate1 } = createMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) - const { mutate: mutate2 } = createMutation(['mutation2'], async () => { - await sleep(100) - return 'data' + const { mutate: mutate2 } = createMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { + await sleep(100) + return 'data' + }, }) createEffect(() => { @@ -176,9 +194,12 @@ describe('useIsMutating', () => { function Page() { const [mounted, setMounted] = createSignal(true) - const { mutate: mutate1 } = createMutation(['mutation1'], async () => { - await sleep(10) - return 'data' + const { mutate: mutate1 } = createMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { + await sleep(10) + return 'data' + }, }) createEffect(() => { @@ -227,22 +248,22 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation( - ['mutation1'], - async () => { + const { mutate: mutate1 } = createMutation({ + mutationKey: ['mutation1'], + mutationFn: async () => { await sleep(150) return 'data' }, - { context }, - ) - const { mutate: mutate2 } = createMutation( - ['mutation2'], - async () => { + context, + }) + const { mutate: mutate2 } = createMutation({ + mutationKey: ['mutation2'], + mutationFn: async () => { await sleep(50) return 'data' }, - { context }, - ) + context, + }) createEffect(() => { mutate1() @@ -275,7 +296,9 @@ describe('useIsMutating', () => { } function Page() { - const { mutate } = createMutation(['mutation'], async () => 'data', { + const { mutate } = createMutation({ + mutationKey: ['mutation'], + mutationFn: async () => 'data', throwErrors: true, context, }) diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts index fbef03ea6c..78eb47c73c 100644 --- a/packages/solid-query/src/createInfiniteQuery.ts +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -1,8 +1,4 @@ -import type { - QueryObserver, - QueryFunction, - QueryOptions, -} from '@tanstack/query-core' +import type { QueryObserver, QueryOptions } from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, @@ -12,84 +8,15 @@ import type { import { createBaseQuery } from './createBaseQuery' import { createComputed } from 'solid-js' import { createStore } from 'solid-js/store' -import { parseQueryArgs } from './utils' +import { normalizeQueryOptions } from './utils' -export function createInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - options: CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, -): CreateInfiniteQueryResult -export function createInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - 'queryKey' - >, -): CreateInfiniteQueryResult -export function createInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - 'queryKey' | 'queryFn' - >, -): CreateInfiniteQueryResult export function createInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, >( - arg1: - | TQueryKey - | CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - arg2?: - | QueryFunction> - | CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, - arg3?: CreateInfiniteQueryOptions< + options: CreateInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -100,12 +27,12 @@ export function createInfiniteQuery< // The parseQuery Args functions helps normalize the arguments into the correct form. // Whatever the parameters are, they are normalized into the correct form. const [parsedOptions, setParsedOptions] = createStore( - parseQueryArgs(arg1, arg2, arg3), + normalizeQueryOptions(options), ) // Watch for changes in the options and update the parsed options. createComputed(() => { - const newParsedOptions = parseQueryArgs(arg1, arg2, arg3) + const newParsedOptions = normalizeQueryOptions(options) setParsedOptions(newParsedOptions) }) diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index 0ebc56ec77..6cb2b6376d 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -1,5 +1,4 @@ -import type { MutationFunction, MutationKey } from '@tanstack/query-core' -import { parseMutationArgs, MutationObserver } from '@tanstack/query-core' +import { MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { CreateMutateFunction, @@ -17,61 +16,9 @@ export function createMutation< TVariables = void, TContext = unknown, >( - options: CreateMutationOptions, -): CreateMutationResult -export function createMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationFn: MutationFunction, - options?: Omit< - CreateMutationOptions, - 'mutationFn' - >, -): CreateMutationResult -export function createMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MutationKey, - options?: Omit< - CreateMutationOptions, - 'mutationKey' - >, -): CreateMutationResult -export function createMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MutationKey, - mutationFn?: MutationFunction, - options?: Omit< - CreateMutationOptions, - 'mutationKey' | 'mutationFn' - >, -): CreateMutationResult -export function createMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - arg1: - | MutationKey - | MutationFunction - | CreateMutationOptions, - arg2?: - | MutationFunction - | CreateMutationOptions, - arg3?: CreateMutationOptions, + mutationOptions: CreateMutationOptions, ): CreateMutationResult { - const [options, setOptions] = createStore(parseMutationArgs(arg1, arg2, arg3)) + const [options, setOptions] = createStore(mutationOptions) const queryClient = useQueryClient({ context: options.context }) const observer = new MutationObserver( @@ -95,9 +42,8 @@ export function createMutation< }) createComputed(() => { - const newParsedOptions = parseMutationArgs(arg1, arg2, arg3) - setOptions(newParsedOptions) - observer.setOptions(newParsedOptions) + setOptions(mutationOptions) + observer.setOptions(mutationOptions) }) createComputed( diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts index 387c63a543..1b48a77d66 100644 --- a/packages/solid-query/src/createQuery.ts +++ b/packages/solid-query/src/createQuery.ts @@ -1,4 +1,4 @@ -import type { QueryFunction, QueryOptions } from '@tanstack/query-core' +import type { QueryOptions } from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' import type { CreateQueryOptions, @@ -8,145 +8,62 @@ import type { } from './types' import { createComputed } from 'solid-js' import { createStore } from 'solid-js/store' -import { parseQueryArgs } from './utils' +import { normalizeQueryOptions } from './utils' import { createBaseQuery } from './createBaseQuery' -// There are several ways to create a query. -// 1. createQuery(options: CreateQueryOptions) -// 2. createQuery(querykey: () => Serializable[], options: CreateQueryOptions) -// 3. createQuery(querykey: () => Serializable[], queryFunc: Fetcher Function, options: CreateQueryOptions) -// 4. The fourth overload is a combination of all three function params -export function createQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - options: Omit< - CreateQueryOptions, - 'initialData' - > & { - initialData?: () => undefined - }, -): CreateQueryResult -export function createQuery< +type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - options: Omit< - CreateQueryOptions, - 'initialData' - > & { - initialData: TQueryFnData | (() => TQueryFnData) - }, -): DefinedCreateQueryResult -export function createQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - options: CreateQueryOptions, -): CreateQueryResult -export function createQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - CreateQueryOptions, - 'queryKey' | 'initialData' - > & { initialData?: () => undefined }, -): CreateQueryResult -export function createQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - CreateQueryOptions, - 'queryKey' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, -): DefinedCreateQueryResult -export function createQuery< +> = CreateQueryOptions & { + initialData?: undefined +} + +type DefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - CreateQueryOptions, - 'queryKey' - >, -): CreateQueryResult +> = CreateQueryOptions & { + initialData: TQueryFnData | (() => TQueryFnData) +} + +// There is one way to create a query. +// 1. createQuery(options: CreateQueryOptions) + export function createQuery< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, >( - queryKey: TQueryKey, - // TODO(lukemurray): not sure if we want to use return type here - queryFn: QueryFunction>, - options?: Omit< - CreateQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData?: () => undefined }, + options: UndefinedInitialDataOptions, ): CreateQueryResult + export function createQuery< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, >( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - CreateQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, + options: DefinedInitialDataOptions, ): DefinedCreateQueryResult -export function createQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - CreateQueryOptions, - 'queryKey' | 'queryFn' - >, -): CreateQueryResult + export function createQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends SolidQueryKey = SolidQueryKey, >( - arg1: TQueryKey | CreateQueryOptions, - arg2?: - | QueryFunction> - | CreateQueryOptions, - arg3?: CreateQueryOptions, + options: CreateQueryOptions, ): CreateQueryResult { - // The parseQuery Args functions helps normalize the arguments into the correct form. - // Whatever the parameters are, they are normalized into the correct form. const [parsedOptions, setParsedOptions] = createStore( - parseQueryArgs(arg1, arg2, arg3), + normalizeQueryOptions(options), ) - // Watch for changes in the options and update the parsed options. createComputed(() => { - const newParsedOptions = parseQueryArgs(arg1, arg2, arg3) + const newParsedOptions = normalizeQueryOptions(options) setParsedOptions(newParsedOptions) }) diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index 817c599e84..2b3e56f0a7 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -13,7 +13,6 @@ import type { InfiniteQueryObserverOptions, InfiniteQueryObserverResult, QueryFilters, - QueryOptions, } from '@tanstack/query-core' export interface ContextOptions { @@ -50,7 +49,7 @@ export interface CreateQueryOptions< >, 'queryKey' > { - queryKey?: TQueryKey + queryKey: TQueryKey } export type CreateBaseQueryResult< @@ -73,18 +72,6 @@ export type DefinedCreateQueryResult< TError = unknown, > = DefinedCreateBaseQueryResult -export type ParseQueryArgs< - TOptions extends Omit< - QueryOptions>, - 'queryKey' - > & { - queryKey?: TQueryKey - }, - TQueryKey extends () => readonly unknown[] = SolidQueryKey, -> = TOptions['queryKey'] extends () => infer R - ? Omit & { queryKey?: R } - : TOptions - /* --- Create Infinite Queries Types --- */ export interface CreateInfiniteQueryOptions< TQueryFnData = unknown, @@ -103,7 +90,7 @@ export interface CreateInfiniteQueryOptions< >, 'queryKey' > { - queryKey?: TQueryKey + queryKey: TQueryKey } export type CreateInfiniteQueryResult< diff --git a/packages/solid-query/src/useIsFetching.ts b/packages/solid-query/src/useIsFetching.ts index 06844a3796..3ff57bb89d 100644 --- a/packages/solid-query/src/useIsFetching.ts +++ b/packages/solid-query/src/useIsFetching.ts @@ -1,28 +1,21 @@ import type { QueryFilters } from '@tanstack/query-core' -import type { ContextOptions, SolidQueryKey, SolidQueryFilters } from './types' +import type { ContextOptions, SolidQueryFilters } from './types' import { useQueryClient } from './QueryClientProvider' import type { Accessor } from 'solid-js' import { createSignal, onCleanup, createComputed, createMemo } from 'solid-js' -import { parseFilterArgs } from './utils' +import { normalizeFilterArgs } from './utils' interface Options extends ContextOptions {} export function useIsFetching( - filters?: SolidQueryFilters, - options?: Options, -): Accessor -export function useIsFetching( - queryKey?: SolidQueryKey, - filters?: SolidQueryFilters, - options?: Options, -): Accessor -export function useIsFetching( - arg1?: SolidQueryKey | SolidQueryFilters, - arg2?: SolidQueryFilters | Options, - arg3?: Options, + filtersArgs?: SolidQueryFilters, + optionsArgs?: Options, ): Accessor { - const [filtersObj, optionsObj = {}] = parseFilterArgs(arg1, arg2, arg3) + const [filtersObj, optionsObj = {}] = normalizeFilterArgs( + filtersArgs, + optionsArgs, + ) const [filters, setFilters] = createSignal(filtersObj) const [options, setOptions] = createSignal(optionsObj) @@ -37,10 +30,9 @@ export function useIsFetching( ) createComputed(() => { - const [newFiltersObj, newOptionsObj = {}] = parseFilterArgs( - arg1, - arg2, - arg3, + const [newFiltersObj, newOptionsObj = {}] = normalizeFilterArgs( + filtersArgs, + optionsArgs, ) setFilters(newFiltersObj) setOptions(newOptionsObj) diff --git a/packages/solid-query/src/useIsMutating.ts b/packages/solid-query/src/useIsMutating.ts index 5fe0bc91ca..d4a2e586bd 100644 --- a/packages/solid-query/src/useIsMutating.ts +++ b/packages/solid-query/src/useIsMutating.ts @@ -1,5 +1,4 @@ -import type { MutationKey, MutationFilters } from '@tanstack/query-core' -import { parseMutationFilterArgs } from '@tanstack/query-core' +import type { MutationFilters } from '@tanstack/query-core' import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' import type { Accessor } from 'solid-js' @@ -9,20 +8,8 @@ interface Options extends ContextOptions {} export function useIsMutating( filters?: MutationFilters, - options?: Options, -): Accessor -export function useIsMutating( - mutationKey?: MutationKey, - filters?: Omit, - options?: Options, -): Accessor -export function useIsMutating( - arg1?: MutationKey | MutationFilters, - arg2?: Omit | Options, - arg3?: Options, + options: Options = {}, ): Accessor { - const [filters, options = {}] = parseMutationFilterArgs(arg1, arg2, arg3) - const queryClient = useQueryClient({ context: options.context }) const mutationCache = queryClient.getMutationCache() diff --git a/packages/solid-query/src/utils.ts b/packages/solid-query/src/utils.ts index 22ee398604..4a04558d76 100644 --- a/packages/solid-query/src/utils.ts +++ b/packages/solid-query/src/utils.ts @@ -1,10 +1,4 @@ -import type { - SolidQueryKey, - SolidQueryFilters, - ParseFilterArgs, - ParseQueryArgs, -} from './types' -import type { QueryFunction, QueryOptions } from '@tanstack/query-core' +import type { SolidQueryKey, SolidQueryFilters, ParseFilterArgs } from './types' export function isQueryKey(value: unknown): value is SolidQueryKey { return typeof value === 'function' @@ -12,50 +6,28 @@ export function isQueryKey(value: unknown): value is SolidQueryKey { // The parseQuery Args functions helps normalize the arguments into the correct form. // Whatever the parameters are, they are normalized into the correct form. -export function parseQueryArgs< - TOptions extends Omit< - QueryOptions>, - 'queryKey' - > & { - queryKey?: TQueryKey - }, - TQueryKey extends () => readonly unknown[] = SolidQueryKey, ->( - arg1: TQueryKey | TOptions, - arg2?: QueryFunction> | TOptions, - arg3?: TOptions, -): ParseQueryArgs { - if (!isQueryKey(arg1)) { - const { queryKey: solidKey, ...opts } = arg1 as any - if (solidKey) { - return { - ...opts, - queryKey: solidKey(), - } +export function normalizeQueryOptions(arg: T): T { + const { queryKey: solidKey, ...opts } = arg as any + if (solidKey) { + return { + ...opts, + queryKey: solidKey(), } - return arg1 as any - } - - if (typeof arg2 === 'function') { - return { ...arg3, queryKey: arg1(), queryFn: arg2 } as any } - - return { ...arg2, queryKey: arg1() } as any + return arg as any } -export function parseFilterArgs< +export function normalizeFilterArgs< TFilters extends SolidQueryFilters, TOptions = unknown, >( - arg1?: SolidQueryKey | TFilters, - arg2?: TFilters | TOptions, - arg3?: TOptions, + arg1?: TFilters, + arg2?: TOptions, ): [ParseFilterArgs, TOptions | undefined] { - return ( - isQueryKey(arg1) - ? [{ ...arg2, queryKey: arg1() }, arg3] - : [{ ...arg1, queryKey: arg1?.queryKey?.() }, arg2] - ) as [ParseFilterArgs, TOptions] + return [{ ...arg1, queryKey: arg1?.queryKey?.() }, arg2] as [ + ParseFilterArgs, + TOptions, + ] } export function shouldThrowError boolean>( diff --git a/packages/vue-query/src/__mocks__/useBaseQuery.ts b/packages/vue-query/src/__mocks__/useBaseQuery.ts index d8f718d93e..83d6d38820 100644 --- a/packages/vue-query/src/__mocks__/useBaseQuery.ts +++ b/packages/vue-query/src/__mocks__/useBaseQuery.ts @@ -1,5 +1,5 @@ -const { useBaseQuery: originImpl, parseQueryArgs: originalParse } = +const { useBaseQuery: originImpl, unrefQueryArgs: originalParse } = jest.requireActual('../useBaseQuery') export const useBaseQuery = jest.fn(originImpl) -export const parseQueryArgs = originalParse +export const unrefQueryArgs = originalParse diff --git a/packages/vue-query/src/__tests__/queryCache.test.ts b/packages/vue-query/src/__tests__/queryCache.test.ts index 7c28b91480..ee7bc9c63a 100644 --- a/packages/vue-query/src/__tests__/queryCache.test.ts +++ b/packages/vue-query/src/__tests__/queryCache.test.ts @@ -13,12 +13,13 @@ describe('QueryCache', () => { test('should properly unwrap parameters', async () => { const queryCache = new QueryCache() - queryCache.find(['foo', ref('bar')], { - queryKey: ref(['baz']), + queryCache.find({ + queryKey: ['foo', ref('bar')], }) - expect(QueryCacheOrigin.prototype.find).toBeCalledWith(['foo', 'bar'], { - queryKey: ['baz'], + expect(QueryCacheOrigin.prototype.find).toBeCalledWith({ + queryKey: ['foo', 'bar'], + exact: true, //Exact is true, as `find` in QueryCacheOrigin sets exact to true in the passed filters if exact is undefined }) }) }) @@ -27,16 +28,13 @@ describe('QueryCache', () => { test('should properly unwrap two parameters', async () => { const queryCache = new QueryCache() - queryCache.findAll(['foo', ref('bar')], { - queryKey: ref(['baz']), + queryCache.findAll({ + queryKey: ['foo', ref('bar')], }) - expect(QueryCacheOrigin.prototype.findAll).toBeCalledWith( - ['foo', 'bar'], - { - queryKey: ['baz'], - }, - ) + expect(QueryCacheOrigin.prototype.findAll).toBeCalledWith({ + queryKey: ['foo', 'bar'], + }) }) test('should properly unwrap one parameter', async () => { diff --git a/packages/vue-query/src/__tests__/queryClient.test.ts b/packages/vue-query/src/__tests__/queryClient.test.ts index 4f6728d4ae..5e4935a365 100644 --- a/packages/vue-query/src/__tests__/queryClient.test.ts +++ b/packages/vue-query/src/__tests__/queryClient.test.ts @@ -28,21 +28,6 @@ describe('QueryCache', () => { queryKey: queryKeyUnref, }) }) - - test('should properly unwrap 2 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.isFetching(queryKeyRef, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.isFetching).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - ) - }) }) describe('isMutating', () => { @@ -60,18 +45,13 @@ describe('QueryCache', () => { }) describe('getQueryData', () => { - test('should properly unwrap 2 parameter', async () => { + test('should properly unwrap 1 parameter', async () => { const queryClient = new QueryClient() - queryClient.getQueryData(queryKeyRef, { - queryKey: queryKeyRef, - }) + queryClient.getQueryData(queryKeyRef) expect(QueryClientOrigin.prototype.getQueryData).toBeCalledWith( queryKeyUnref, - { - queryKey: queryKeyUnref, - }, ) }) }) @@ -80,11 +60,11 @@ describe('QueryCache', () => { test('should properly unwrap queryKey param', async () => { const queryClient = new QueryClient() - queryClient.getQueriesData(queryKeyRef) + queryClient.getQueriesData({ queryKey: queryKeyRef }) - expect(QueryClientOrigin.prototype.getQueriesData).toBeCalledWith( - queryKeyUnref, - ) + expect(QueryClientOrigin.prototype.getQueriesData).toBeCalledWith({ + queryKey: queryKeyUnref, + }) }) test('should properly unwrap filters param', async () => { @@ -102,7 +82,9 @@ describe('QueryCache', () => { test('should properly unwrap 3 parameter', async () => { const queryClient = new QueryClient() - queryClient.setQueryData(queryKeyRef, fn, { updatedAt: ref(3) }) + queryClient.setQueryData(queryKeyRef, fn, { + updatedAt: ref(3), + }) expect(QueryClientOrigin.prototype.setQueryData).toBeCalledWith( queryKeyUnref, @@ -116,10 +98,12 @@ describe('QueryCache', () => { test('should properly unwrap params with queryKey', async () => { const queryClient = new QueryClient() - queryClient.setQueriesData(queryKeyRef, fn, { updatedAt: ref(3) }) + queryClient.setQueriesData({ queryKey: queryKeyRef }, fn, { + updatedAt: ref(3), + }) expect(QueryClientOrigin.prototype.setQueriesData).toBeCalledWith( - queryKeyUnref, + { queryKey: queryKeyUnref }, fn, { updatedAt: 3 }, ) @@ -141,14 +125,13 @@ describe('QueryCache', () => { }) describe('getQueryState', () => { - test('should properly unwrap 2 parameter', async () => { + test('should properly unwrap 1 parameter', async () => { const queryClient = new QueryClient() - queryClient.getQueryState(queryKeyRef, { queryKey: queryKeyRef }) + queryClient.getQueryState(queryKeyRef) expect(QueryClientOrigin.prototype.getQueryState).toBeCalledWith( queryKeyUnref, - { queryKey: queryKeyUnref }, ) }) }) @@ -165,21 +148,6 @@ describe('QueryCache', () => { queryKey: queryKeyUnref, }) }) - - test('should properly unwrap 2 parameter', async () => { - const queryClient = new QueryClient() - - queryClient.removeQueries(queryKeyRef, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.removeQueries).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - ) - }) }) describe('resetQueries', () => { @@ -200,26 +168,6 @@ describe('QueryCache', () => { { cancelRefetch: false }, ) }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.resetQueries( - queryKeyRef, - { - queryKey: queryKeyRef, - }, - { cancelRefetch: ref(false) }, - ) - - expect(QueryClientOrigin.prototype.resetQueries).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - { cancelRefetch: false }, - ) - }) }) describe('cancelQueries', () => { @@ -240,26 +188,6 @@ describe('QueryCache', () => { { revert: false }, ) }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.cancelQueries( - queryKeyRef, - { - queryKey: queryKeyRef, - }, - { revert: ref(false) }, - ) - - expect(QueryClientOrigin.prototype.cancelQueries).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - { revert: false }, - ) - }) }) describe('invalidateQueries', () => { @@ -280,26 +208,6 @@ describe('QueryCache', () => { { cancelRefetch: false }, ) }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.invalidateQueries( - queryKeyRef, - { - queryKey: queryKeyRef, - }, - { cancelRefetch: ref(false) }, - ) - - expect(QueryClientOrigin.prototype.invalidateQueries).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - { cancelRefetch: false }, - ) - }) }) describe('refetchQueries', () => { @@ -320,30 +228,10 @@ describe('QueryCache', () => { { cancelRefetch: false }, ) }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.refetchQueries( - queryKeyRef, - { - queryKey: queryKeyRef, - }, - { cancelRefetch: ref(false) }, - ) - - expect(QueryClientOrigin.prototype.refetchQueries).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - { cancelRefetch: false }, - ) - }) }) describe('fetchQuery', () => { - test('should properly unwrap 1 parameter', async () => { + test('should properly unwrap parameter', async () => { const queryClient = new QueryClient() queryClient.fetchQuery({ @@ -354,58 +242,23 @@ describe('QueryCache', () => { queryKey: queryKeyUnref, }) }) - - test('should properly unwrap 2 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.fetchQuery(queryKeyRef, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.fetchQuery).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - undefined, - ) - }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.fetchQuery(queryKeyRef, fn, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.fetchQuery).toBeCalledWith( - queryKeyUnref, - fn, - { - queryKey: queryKeyUnref, - }, - ) - }) }) describe('prefetchQuery', () => { test('should properly unwrap parameters', async () => { const queryClient = new QueryClient() - queryClient.prefetchQuery(queryKeyRef, fn, { queryKey: queryKeyRef }) + queryClient.prefetchQuery({ queryKey: queryKeyRef, queryFn: fn }) - expect(QueryClientOrigin.prototype.prefetchQuery).toBeCalledWith( - queryKeyUnref, - fn, - { - queryKey: queryKeyUnref, - }, - ) + expect(QueryClientOrigin.prototype.prefetchQuery).toBeCalledWith({ + queryKey: queryKeyUnref, + queryFn: fn, + }) }) }) describe('fetchInfiniteQuery', () => { - test('should properly unwrap 1 parameter', async () => { + test('should properly unwrap parameter', async () => { const queryClient = new QueryClient() queryClient.fetchInfiniteQuery({ @@ -416,55 +269,21 @@ describe('QueryCache', () => { queryKey: queryKeyUnref, }) }) - - test('should properly unwrap 2 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.fetchInfiniteQuery(queryKeyRef, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.fetchInfiniteQuery).toBeCalledWith( - queryKeyUnref, - { - queryKey: queryKeyUnref, - }, - undefined, - ) - }) - - test('should properly unwrap 3 parameters', async () => { - const queryClient = new QueryClient() - - queryClient.fetchInfiniteQuery(queryKeyRef, fn, { - queryKey: queryKeyRef, - }) - - expect(QueryClientOrigin.prototype.fetchInfiniteQuery).toBeCalledWith( - queryKeyUnref, - fn, - { - queryKey: queryKeyUnref, - }, - ) - }) }) describe('prefetchInfiniteQuery', () => { test('should properly unwrap parameters', async () => { const queryClient = new QueryClient() - queryClient.prefetchInfiniteQuery(queryKeyRef, fn, { + queryClient.prefetchInfiniteQuery({ queryKey: queryKeyRef, + queryFn: fn, }) - expect(QueryClientOrigin.prototype.prefetchInfiniteQuery).toBeCalledWith( - queryKeyUnref, - fn, - { - queryKey: queryKeyUnref, - }, - ) + expect(QueryClientOrigin.prototype.prefetchInfiniteQuery).toBeCalledWith({ + queryKey: queryKeyUnref, + queryFn: fn, + }) }) }) diff --git a/packages/vue-query/src/__tests__/useInfiniteQuery.test.ts b/packages/vue-query/src/__tests__/useInfiniteQuery.test.ts index f887f3ce1e..0e3f1dad79 100644 --- a/packages/vue-query/src/__tests__/useInfiniteQuery.test.ts +++ b/packages/vue-query/src/__tests__/useInfiniteQuery.test.ts @@ -5,10 +5,10 @@ jest.mock('../useQueryClient') describe('useQuery', () => { test('should properly execute infinite query', async () => { - const { data, fetchNextPage, status } = useInfiniteQuery( - ['infiniteQuery'], - infiniteFetcher, - ) + const { data, fetchNextPage, status } = useInfiniteQuery({ + queryKey: ['infiniteQuery'], + queryFn: infiniteFetcher, + }) expect(data.value).toStrictEqual(undefined) expect(status.value).toStrictEqual('loading') diff --git a/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx b/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx index d17328f02d..7e33a28ed7 100644 --- a/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx +++ b/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx @@ -8,6 +8,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useInfiniteQuery({ + queryKey: ['infiniteQuery'], queryFn: simpleFetcher, }), ) @@ -23,6 +24,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useInfiniteQuery({ + queryKey: ['infiniteQuery'], queryFn: simpleFetcher, }), ) @@ -40,6 +42,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useInfiniteQuery({ + queryKey: ['infiniteQuery'], queryFn: simpleFetcher, }), ) @@ -56,6 +59,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useInfiniteQuery({ + queryKey: ['infiniteQuery'], queryFn: simpleFetcher, }), ) @@ -72,6 +76,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useInfiniteQuery({ + queryKey: ['infiniteQuery'], queryFn: simpleFetcher, }), ) diff --git a/packages/vue-query/src/__tests__/useIsFetching.test.ts b/packages/vue-query/src/__tests__/useIsFetching.test.ts index d90be4cf62..c1d15b67e4 100644 --- a/packages/vue-query/src/__tests__/useIsFetching.test.ts +++ b/packages/vue-query/src/__tests__/useIsFetching.test.ts @@ -2,17 +2,17 @@ import { onScopeDispose, reactive, ref } from 'vue-demi' import { flushPromises, simpleFetcher } from './test-utils' import { useQuery } from '../useQuery' -import { parseFilterArgs, useIsFetching } from '../useIsFetching' +import { unrefFilterArgs, useIsFetching } from '../useIsFetching' jest.mock('../useQueryClient') describe('useIsFetching', () => { test('should properly return isFetching state', async () => { - const { isFetching: isFetchingQuery } = useQuery( - ['isFetching1'], - simpleFetcher, - ) - useQuery(['isFetching2'], simpleFetcher) + const { isFetching: isFetchingQuery } = useQuery({ + queryKey: ['isFetching1'], + queryFn: simpleFetcher, + }) + useQuery({ queryKey: ['isFetching2'], queryFn: simpleFetcher }) const isFetching = useIsFetching() expect(isFetchingQuery.value).toStrictEqual(true) @@ -30,7 +30,10 @@ describe('useIsFetching', () => { > onScopeDisposeMock.mockImplementation((fn) => fn()) - const { status } = useQuery(['onScopeDispose'], simpleFetcher) + const { status } = useQuery({ + queryKey: ['onScopeDispose'], + queryFn: simpleFetcher, + }) const isFetching = useIsFetching() expect(status.value).toStrictEqual('loading') @@ -51,15 +54,15 @@ describe('useIsFetching', () => { test('should properly update filters', async () => { const filter = reactive({ stale: false }) - useQuery( - ['isFetching'], - () => + useQuery({ + queryKey: ['isFetching'], + queryFn: () => new Promise((resolve) => { setTimeout(() => { return resolve('Some data') }, 100) }), - ) + }) const isFetching = useIsFetching(filter) expect(isFetching.value).toStrictEqual(0) @@ -73,16 +76,10 @@ describe('useIsFetching', () => { }) describe('parseFilterArgs', () => { - test('should default to empty filters', () => { - const result = parseFilterArgs(undefined) - - expect(result).toEqual({}) - }) - test('should merge query key with filters', () => { - const filters = { stale: true } + const filters = { queryKey: ['key'], stale: true } - const result = parseFilterArgs(['key'], filters) + const result = unrefFilterArgs(filters) const expected = { ...filters, queryKey: ['key'] } expect(result).toEqual(expected) @@ -90,9 +87,9 @@ describe('useIsFetching', () => { test('should unwrap refs arguments', () => { const key = ref(['key']) - const filters = ref({ stale: ref(true) }) + const filters = ref({ queryKey: key, stale: ref(true) }) - const result = parseFilterArgs(key, filters) + const result = unrefFilterArgs(filters) const expected = { queryKey: ['key'], stale: true } expect(result).toEqual(expected) diff --git a/packages/vue-query/src/__tests__/useIsMutating.test.ts b/packages/vue-query/src/__tests__/useIsMutating.test.ts index afc96654e2..8aff85ff57 100644 --- a/packages/vue-query/src/__tests__/useIsMutating.test.ts +++ b/packages/vue-query/src/__tests__/useIsMutating.test.ts @@ -2,15 +2,19 @@ import { onScopeDispose, reactive, ref } from 'vue-demi' import { flushPromises, successMutator } from './test-utils' import { useMutation } from '../useMutation' -import { parseFilterArgs, useIsMutating } from '../useIsMutating' +import { unrefFilterArgs, useIsMutating } from '../useIsMutating' import { useQueryClient } from '../useQueryClient' jest.mock('../useQueryClient') describe('useIsMutating', () => { test('should properly return isMutating state', async () => { - const mutation = useMutation((params: string) => successMutator(params)) - const mutation2 = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) + const mutation2 = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) const isMutating = useIsMutating() expect(isMutating.value).toStrictEqual(0) @@ -33,8 +37,12 @@ describe('useIsMutating', () => { > onScopeDisposeMock.mockImplementation((fn) => fn()) - const mutation = useMutation((params: string) => successMutator(params)) - const mutation2 = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) + const mutation2 = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) const isMutating = useIsMutating() expect(isMutating.value).toStrictEqual(0) @@ -62,9 +70,10 @@ describe('useIsMutating', () => { test('should properly update filters', async () => { const filter = reactive({ mutationKey: ['foo'] }) - const { mutate } = useMutation(['isMutating'], (params: string) => - successMutator(params), - ) + const { mutate } = useMutation({ + mutationKey: ['isMutating'], + mutationFn: (params: string) => successMutator(params), + }) mutate('foo') const isMutating = useIsMutating(filter) @@ -79,16 +88,10 @@ describe('useIsMutating', () => { }) describe('parseMutationFilterArgs', () => { - test('should default to empty filters', () => { - const result = parseFilterArgs(undefined) - - expect(result).toEqual({}) - }) - test('should merge mutation key with filters', () => { - const filters = { fetching: true } + const filters = { mutationKey: ['key'], fetching: true } - const result = parseFilterArgs(['key'], filters) + const result = unrefFilterArgs(filters) const expected = { ...filters, mutationKey: ['key'] } expect(result).toEqual(expected) @@ -96,9 +99,9 @@ describe('useIsMutating', () => { test('should unwrap refs arguments', () => { const key = ref(['key']) - const filters = ref({ fetching: ref(true) }) + const filters = ref({ mutationKey: key, fetching: ref(true) }) - const result = parseFilterArgs(key, filters) + const result = unrefFilterArgs(filters) const expected = { mutationKey: ['key'], fetching: true } expect(result).toEqual(expected) diff --git a/packages/vue-query/src/__tests__/useMutation.test.ts b/packages/vue-query/src/__tests__/useMutation.test.ts index 67e4e85ab9..770bfbbe4f 100644 --- a/packages/vue-query/src/__tests__/useMutation.test.ts +++ b/packages/vue-query/src/__tests__/useMutation.test.ts @@ -1,13 +1,15 @@ import { reactive, ref } from 'vue-demi' import { errorMutator, flushPromises, successMutator } from './test-utils' -import { parseMutationArgs, useMutation } from '../useMutation' +import { unrefMutationArgs, useMutation } from '../useMutation' import { useQueryClient } from '../useQueryClient' jest.mock('../useQueryClient') describe('useMutation', () => { test('should be in idle state initially', () => { - const mutation = useMutation((params) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params) => successMutator(params), + }) expect(mutation).toMatchObject({ isIdle: { value: true }, @@ -19,7 +21,9 @@ describe('useMutation', () => { test('should change state after invoking mutate', () => { const result = 'Mock data' - const mutation = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) mutation.mutate(result) @@ -34,7 +38,7 @@ describe('useMutation', () => { }) test('should return error when request fails', async () => { - const mutation = useMutation(errorMutator) + const mutation = useMutation({ mutationFn: errorMutator }) mutation.mutate({}) await flushPromises(10) expect(mutation).toMatchObject({ @@ -49,7 +53,9 @@ describe('useMutation', () => { test('should return data when request succeeds', async () => { const result = 'Mock data' - const mutation = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) mutation.mutate(result) @@ -68,11 +74,11 @@ describe('useMutation', () => { test('should update reactive options', async () => { const queryClient = useQueryClient() const mutationCache = queryClient.getMutationCache() - const options = reactive({ mutationKey: ['foo'] }) - const mutation = useMutation( - (params: string) => successMutator(params), - options, - ) + const options = reactive({ + mutationKey: ['foo'], + mutationFn: (params: string) => successMutator(params), + }) + const mutation = useMutation(options) options.mutationKey = ['bar'] await flushPromises() @@ -100,11 +106,11 @@ describe('useMutation', () => { ]) const queryClient = useQueryClient() const mutationCache = queryClient.getMutationCache() - const options = reactive({ mutationKey }) - const mutation = useMutation( - (params: string) => successMutator(params), - options, - ) + const options = reactive({ + mutationKey, + mutationFn: (params: string) => successMutator(params), + }) + const mutation = useMutation(options) mutationKey.value[0]!.otherObject.name = 'someOtherObjectName' await flushPromises() @@ -131,7 +137,7 @@ describe('useMutation', () => { const mutationFn = ref((params: string) => successMutator(params)) const queryClient = useQueryClient() const mutationCache = queryClient.getMutationCache() - const mutation = useMutation(mutationKey, mutationFn) + const mutation = useMutation({ mutationKey, mutationFn }) mutationKey.value = ['bar2'] let proof = false @@ -150,7 +156,9 @@ describe('useMutation', () => { }) test('should reset state after invoking mutation.reset', async () => { - const mutation = useMutation((params: string) => errorMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => errorMutator(params), + }) mutation.mutate('') @@ -175,7 +183,8 @@ describe('useMutation', () => { test('should call onMutate when passed as an option', async () => { const onMutate = jest.fn() - const mutation = useMutation((params: string) => successMutator(params), { + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), onMutate, }) @@ -188,7 +197,8 @@ describe('useMutation', () => { test('should call onError when passed as an option', async () => { const onError = jest.fn() - const mutation = useMutation((params: string) => errorMutator(params), { + const mutation = useMutation({ + mutationFn: (params: string) => errorMutator(params), onError, }) @@ -201,7 +211,8 @@ describe('useMutation', () => { test('should call onSuccess when passed as an option', async () => { const onSuccess = jest.fn() - const mutation = useMutation((params: string) => successMutator(params), { + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), onSuccess, }) @@ -214,7 +225,8 @@ describe('useMutation', () => { test('should call onSettled when passed as an option', async () => { const onSettled = jest.fn() - const mutation = useMutation((params: string) => successMutator(params), { + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), onSettled, }) @@ -227,7 +239,9 @@ describe('useMutation', () => { test('should call onError when passed as an argument of mutate function', async () => { const onError = jest.fn() - const mutation = useMutation((params: string) => errorMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => errorMutator(params), + }) mutation.mutate('', { onError }) @@ -238,7 +252,9 @@ describe('useMutation', () => { test('should call onSuccess when passed as an argument of mutate function', async () => { const onSuccess = jest.fn() - const mutation = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) mutation.mutate('', { onSuccess }) @@ -249,7 +265,9 @@ describe('useMutation', () => { test('should call onSettled when passed as an argument of mutate function', async () => { const onSettled = jest.fn() - const mutation = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) mutation.mutate('', { onSettled }) @@ -261,7 +279,8 @@ describe('useMutation', () => { test('should fire both onSettled functions', async () => { const onSettled = jest.fn() const onSettledOnFunction = jest.fn() - const mutation = useMutation((params: string) => successMutator(params), { + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), onSettled, }) @@ -281,7 +300,9 @@ describe('useMutation', () => { test('should resolve properly', async () => { const result = 'Mock data' - const mutation = useMutation((params: string) => successMutator(params)) + const mutation = useMutation({ + mutationFn: (params: string) => successMutator(params), + }) await expect(mutation.mutateAsync(result)).resolves.toBe(result) @@ -296,7 +317,7 @@ describe('useMutation', () => { }) test('should throw on error', async () => { - const mutation = useMutation(errorMutator) + const mutation = useMutation({ mutationFn: errorMutator }) await expect(mutation.mutateAsync({})).rejects.toThrowError('Some error') @@ -314,30 +335,34 @@ describe('useMutation', () => { describe('parseMutationArgs', () => { test('should return the same instance of options', () => { const options = { retry: false } - const result = parseMutationArgs(options) + const result = unrefMutationArgs(options) expect(result).toEqual(options) }) test('should merge query key with options', () => { - const options = { retry: false } - const result = parseMutationArgs(['key'], options) + const options = { mutationKey: ['key'], retry: false } + const result = unrefMutationArgs(options) const expected = { ...options, mutationKey: ['key'] } expect(result).toEqual(expected) }) test('should merge query fn with options', () => { - const options = { retry: false } - const result = parseMutationArgs(successMutator, options) + const options = { mutationFn: successMutator, retry: false } + const result = unrefMutationArgs(options) const expected = { ...options, mutationFn: successMutator } expect(result).toEqual(expected) }) test('should merge query key and fn with options', () => { - const options = { retry: false } - const result = parseMutationArgs(['key'], successMutator, options) + const options = { + mutationKey: ['key'], + mutationFn: successMutator, + retry: false, + } + const result = unrefMutationArgs(options) const expected = { ...options, mutationKey: ['key'], @@ -350,9 +375,9 @@ describe('useMutation', () => { test('should unwrap refs arguments', () => { const key = ref(['key']) const mutationFn = ref(successMutator) - const options = ref({ retry: ref(12) }) + const options = ref({ mutationKey: key, mutationFn, retry: ref(12) }) - const result = parseMutationArgs(key, mutationFn, options) + const result = unrefMutationArgs(options) const expected = { mutationKey: ['key'], mutationFn: successMutator, diff --git a/packages/vue-query/src/__tests__/useQuery.test.ts b/packages/vue-query/src/__tests__/useQuery.test.ts index 7bb51a6600..d2baf903f3 100644 --- a/packages/vue-query/src/__tests__/useQuery.test.ts +++ b/packages/vue-query/src/__tests__/useQuery.test.ts @@ -14,27 +14,24 @@ import { getSimpleFetcherWithReturnData, } from './test-utils' import { useQuery } from '../useQuery' -import { parseQueryArgs, useBaseQuery } from '../useBaseQuery' +import { unrefQueryArgs, useBaseQuery } from '../useBaseQuery' jest.mock('../useQueryClient') jest.mock('../useBaseQuery') describe('useQuery', () => { test('should properly execute query', () => { - useQuery(['key0'], simpleFetcher, { staleTime: 1000 }) + useQuery({ queryKey: ['key0'], queryFn: simpleFetcher, staleTime: 1000 }) - expect(useBaseQuery).toBeCalledWith( - QueryObserver, - ['key0'], - simpleFetcher, - { - staleTime: 1000, - }, - ) + expect(useBaseQuery).toBeCalledWith(QueryObserver, { + queryKey: ['key0'], + queryFn: simpleFetcher, + staleTime: 1000, + }) }) test('should return loading status initially', () => { - const query = useQuery(['key1'], simpleFetcher) + const query = useQuery({ queryKey: ['key1'], queryFn: simpleFetcher }) expect(query).toMatchObject({ status: { value: 'loading' }, @@ -45,7 +42,10 @@ describe('useQuery', () => { }) test('should resolve to success and update reactive state: useQuery(key, dataFn)', async () => { - const query = useQuery(['key2'], getSimpleFetcherWithReturnData('result2')) + const query = useQuery({ + queryKey: ['key2'], + queryFn: getSimpleFetcherWithReturnData('result2'), + }) await flushPromises() @@ -79,7 +79,8 @@ describe('useQuery', () => { }) test('should resolve to success and update reactive state: useQuery(key, optionsObj)', async () => { - const query = useQuery(['key32'], { + const query = useQuery({ + queryKey: ['key32'], queryFn: getSimpleFetcherWithReturnData('result32'), enabled: true, }) @@ -97,7 +98,10 @@ describe('useQuery', () => { }) test('should reject and update reactive state', async () => { - const query = useQuery(['key3'], rejectFetcher) + const query = useQuery({ + queryKey: ['key3'], + queryFn: rejectFetcher, + }) await flushPromises() @@ -120,9 +124,9 @@ describe('useQuery', () => { // Noop }) useQuery( - ['key6'], - simpleFetcher, reactive({ + queryKey: ['key6'], + queryFn: simpleFetcher, onSuccess, staleTime: 1000, }), @@ -137,7 +141,10 @@ describe('useQuery', () => { test('should update query on reactive (Ref) key change', async () => { const secondKeyRef = ref('key7') - const query = useQuery(['key6', secondKeyRef], simpleFetcher) + const query = useQuery({ + queryKey: ['key6', secondKeyRef], + queryFn: simpleFetcher, + }) await flushPromises() @@ -162,7 +169,11 @@ describe('useQuery', () => { test("should update query when an option is passed as Ref and it's changed", async () => { const enabled = ref(false) - const query = useQuery(['key9'], simpleFetcher, { enabled }) + const query = useQuery({ + queryKey: ['key9'], + queryFn: simpleFetcher, + enabled, + }) await flushPromises() @@ -188,14 +199,17 @@ describe('useQuery', () => { }) test('should properly execute dependant queries', async () => { - const { data } = useQuery(['dependant1'], simpleFetcher) + const { data } = useQuery({ + queryKey: ['dependant1'], + queryFn: simpleFetcher, + }) const enabled = computed(() => !!data.value) const { fetchStatus, status } = useQuery( - ['dependant2'], - simpleFetcher, reactive({ + queryKey: ['dependant2'], + queryFn: simpleFetcher, enabled, }), ) @@ -220,7 +234,10 @@ describe('useQuery', () => { > onScopeDisposeMock.mockImplementationOnce((fn) => fn()) - const { status } = useQuery(['onScopeDispose'], simpleFetcher) + const { status } = useQuery({ + queryKey: ['onScopeDispose'], + queryFn: simpleFetcher, + }) expect(status.value).toStrictEqual('loading') @@ -234,42 +251,12 @@ describe('useQuery', () => { }) describe('parseQueryArgs', () => { - test('should unwrap refs arguments', () => { - const key = ref(['key']) - const fn = ref(simpleFetcher) - const options = ref({ enabled: ref(true) }) - - const result = parseQueryArgs(key, fn, options) - const expected = { - queryKey: ['key'], - queryFn: simpleFetcher, - enabled: true, - } - - expect(result).toEqual(expected) - }) - - test('should unwrap refs with fn in options', () => { - const key = ref(['key']) - const fn = ref(simpleFetcher) - const options = ref({ queryFn: fn, enabled: ref(true) }) - - const result = parseQueryArgs(key, options) - const expected = { - queryKey: ['key'], - queryFn: simpleFetcher, - enabled: true, - } - - expect(result).toEqual(expected) - }) - test('should unwrap refs in options', () => { const key = ref(['key']) const fn = ref(simpleFetcher) const options = ref({ queryKey: key, queryFn: fn, enabled: ref(true) }) - const result = parseQueryArgs(options) + const result = unrefQueryArgs(options) const expected = { queryKey: ['key'], queryFn: simpleFetcher, @@ -285,7 +272,7 @@ describe('useQuery', () => { const getCurrentInstanceSpy = getCurrentInstance as jest.Mock getCurrentInstanceSpy.mockImplementation(() => ({ suspense: {} })) - const query = useQuery(['suspense'], simpleFetcher) + const query = useQuery({ queryKey: ['suspense'], queryFn: simpleFetcher }) const result = query.suspense() expect(result).toBeInstanceOf(Promise) @@ -297,7 +284,9 @@ describe('useQuery', () => { let afterTimeout = false const isEnabeld = ref(false) - const query = useQuery(['suspense'], simpleFetcher, { + const query = useQuery({ + queryKey: ['suspense'], + queryFn: simpleFetcher, enabled: isEnabeld, }) @@ -318,7 +307,9 @@ describe('useQuery', () => { const fetcherSpy = jest.fn(() => simpleFetcher()) // let afterTimeout = false; - const query = useQuery(['suspense'], simpleFetcher, { + const query = useQuery({ + queryKey: ['suspense'], + queryFn: simpleFetcher, staleTime: 10000, initialData: 'foo', }) diff --git a/packages/vue-query/src/__tests__/useQuery.types.test.tsx b/packages/vue-query/src/__tests__/useQuery.types.test.tsx index eb8840b682..d0351a2ab4 100644 --- a/packages/vue-query/src/__tests__/useQuery.types.test.tsx +++ b/packages/vue-query/src/__tests__/useQuery.types.test.tsx @@ -7,6 +7,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useQuery({ + queryKey: ['key'], queryFn: simpleFetcher, }), ) @@ -20,6 +21,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useQuery({ + queryKey: ['key'], queryFn: simpleFetcher, }), ) @@ -36,6 +38,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useQuery({ + queryKey: ['key'], queryFn: simpleFetcher, }), ) @@ -52,6 +55,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useQuery({ + queryKey: ['key'], queryFn: simpleFetcher, }), ) @@ -68,6 +72,7 @@ describe('Discriminated union return type', () => { doNotExecute(() => { const query = reactive( useQuery({ + queryKey: ['key'], queryFn: simpleFetcher, }), ) diff --git a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts index 0902778af4..196222da57 100644 --- a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts +++ b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts @@ -316,7 +316,9 @@ describe('VueQueryPlugin', () => { const fnSpy = jest.fn() - const query = useQuery(['persist'], fnSpy, { + const query = useQuery({ + queryKey: ['persist'], + queryFn: fnSpy, queryClient: customClient, }) diff --git a/packages/vue-query/src/devtools/devtools.ts b/packages/vue-query/src/devtools/devtools.ts index 4b59a58279..972af2489a 100644 --- a/packages/vue-query/src/devtools/devtools.ts +++ b/packages/vue-query/src/devtools/devtools.ts @@ -72,7 +72,7 @@ export function setupDevtools(app: any, queryClient: QueryClient) { tooltip: 'Invalidate', action: (queryHash: string) => { const query = queryCache.get(queryHash) as Query - queryClient.invalidateQueries(query.queryKey) + queryClient.invalidateQueries(query) }, }, { diff --git a/packages/vue-query/src/queryCache.ts b/packages/vue-query/src/queryCache.ts index 04cfaad86c..3012ad92f9 100644 --- a/packages/vue-query/src/queryCache.ts +++ b/packages/vue-query/src/queryCache.ts @@ -1,36 +1,21 @@ import { QueryCache as QC } from '@tanstack/query-core' -import type { Query, QueryKey, QueryFilters } from '@tanstack/query-core' +import type { Query, QueryFilters, WithRequired } from '@tanstack/query-core' import type { MaybeRefDeep } from './types' -import { cloneDeepUnref, isQueryKey } from './utils' +import { cloneDeepUnref } from './utils' export class QueryCache extends QC { find( - arg1: MaybeRefDeep, - arg2?: MaybeRefDeep, + filters: MaybeRefDeep>, ): Query | undefined { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) as QueryFilters - return super.find(arg1Unreffed, arg2Unreffed) + const filtersUnreffed = cloneDeepUnref(filters) as WithRequired< + QueryFilters, + 'queryKey' + > + return super.find(filtersUnreffed) } - findAll( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep, - ): Query[] - findAll(filters?: MaybeRefDeep): Query[] - findAll( - arg1?: MaybeRefDeep, - arg2?: MaybeRefDeep, - ): Query[] - findAll( - arg1?: MaybeRefDeep | MaybeRefDeep, - arg2?: MaybeRefDeep, - ): Query[] { - const arg1Unreffed = cloneDeepUnref(arg1) as QueryKey | QueryFilters - const arg2Unreffed = cloneDeepUnref(arg2) as QueryFilters - if (isQueryKey(arg1Unreffed)) { - return super.findAll(arg1Unreffed, arg2Unreffed) - } - return super.findAll(arg1Unreffed) + findAll(filters?: MaybeRefDeep): Query[] { + const filtersUnreffed = cloneDeepUnref(filters) as QueryFilters + return super.findAll(filtersUnreffed) } } diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 91f7b9c3ee..d796ccf813 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -12,7 +12,6 @@ import type { RefetchQueryFilters, RefetchOptions, FetchQueryOptions, - QueryFunction, FetchInfiniteQueryOptions, InfiniteData, DefaultOptions, @@ -25,7 +24,7 @@ import type { Updater, } from '@tanstack/query-core' import type { MaybeRefDeep } from './types' -import { cloneDeepUnref, isQueryKey } from './utils' +import { cloneDeepUnref } from './utils' import { QueryCache } from './queryCache' import { MutationCache } from './mutationCache' @@ -43,21 +42,9 @@ export class QueryClient extends QC { isRestoring = ref(false) - isFetching(filters?: MaybeRefDeep): number - isFetching( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep, - ): number - isFetching( - arg1?: MaybeRefDeep, - arg2?: MaybeRefDeep, - ): number { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) as QueryFilters - if (isQueryKey(arg1Unreffed)) { - return super.isFetching(arg1Unreffed, arg2Unreffed) - } - return super.isFetching(arg1Unreffed as QueryFilters) + isFetching(filters?: MaybeRefDeep): number { + const filtersUnreffed = cloneDeepUnref(filters) as QueryFilters + return super.isFetching(filtersUnreffed) } isMutating(filters?: MaybeRefDeep): number { @@ -66,28 +53,15 @@ export class QueryClient extends QC { getQueryData( queryKey: MaybeRefDeep, - filters?: MaybeRefDeep, ): TData | undefined { - return super.getQueryData( - cloneDeepUnref(queryKey), - cloneDeepUnref(filters) as QueryFilters, - ) + return super.getQueryData(cloneDeepUnref(queryKey) as QueryKey) } - getQueriesData( - queryKey: MaybeRefDeep, - ): [QueryKey, TData | undefined][] getQueriesData( filters: MaybeRefDeep, - ): [QueryKey, TData | undefined][] - getQueriesData( - queryKeyOrFilters: MaybeRefDeep | MaybeRefDeep, ): [QueryKey, TData | undefined][] { - const unreffed = cloneDeepUnref(queryKeyOrFilters) - if (isQueryKey(unreffed)) { - return super.getQueriesData(unreffed) - } - return super.getQueriesData(unreffed as QueryFilters) + const unreffed = cloneDeepUnref(filters) as QueryFilters + return super.getQueriesData(unreffed) } setQueryData( @@ -96,216 +70,84 @@ export class QueryClient extends QC { options?: MaybeRefDeep, ): TData | undefined { return super.setQueryData( - cloneDeepUnref(queryKey), + cloneDeepUnref(queryKey) as QueryKey, updater, cloneDeepUnref(options) as SetDataOptions, ) } - setQueriesData( - queryKey: MaybeRefDeep, - updater: Updater, - options?: MaybeRefDeep, - ): [QueryKey, TData | undefined][] setQueriesData( filters: MaybeRefDeep, updater: Updater, options?: MaybeRefDeep, - ): [QueryKey, TData | undefined][] - setQueriesData( - queryKeyOrFilters: MaybeRefDeep, - updater: Updater, - options?: MaybeRefDeep, ): [QueryKey, TData | undefined][] { - const arg1Unreffed = cloneDeepUnref(queryKeyOrFilters) - const arg3Unreffed = cloneDeepUnref(options) as SetDataOptions - if (isQueryKey(arg1Unreffed)) { - return super.setQueriesData(arg1Unreffed, updater, arg3Unreffed) - } - return super.setQueriesData( - arg1Unreffed as QueryFilters, - updater, - arg3Unreffed, - ) + const filtersUnreffed = cloneDeepUnref(filters) as QueryFilters + const optionsUnreffed = cloneDeepUnref(options) as SetDataOptions + + return super.setQueriesData(filtersUnreffed, updater, optionsUnreffed) } getQueryState( queryKey: MaybeRefDeep, - filters?: MaybeRefDeep, ): QueryState | undefined { - return super.getQueryState( - cloneDeepUnref(queryKey), - cloneDeepUnref(filters) as QueryFilters, - ) + return super.getQueryState(cloneDeepUnref(queryKey) as QueryKey) } - removeQueries(filters?: MaybeRefDeep): void - removeQueries( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep, - ): void - removeQueries( - arg1?: MaybeRefDeep, - arg2?: MaybeRefDeep, - ): void { - const arg1Unreffed = cloneDeepUnref(arg1) - if (isQueryKey(arg1Unreffed)) { - return super.removeQueries( - arg1Unreffed, - cloneDeepUnref(arg2) as QueryFilters, - ) - } - return super.removeQueries(arg1Unreffed as QueryFilters) + removeQueries(filters?: MaybeRefDeep): void { + const filtersUnreffed = cloneDeepUnref(filters) as QueryFilters + return super.removeQueries(filtersUnreffed) } resetQueries( filters?: MaybeRefDeep>, options?: MaybeRefDeep, - ): Promise - resetQueries( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep>, - options?: MaybeRefDeep, - ): Promise - resetQueries( - arg1?: MaybeRefDeep>, - arg2?: MaybeRefDeep | ResetOptions>, - arg3?: MaybeRefDeep, ): Promise { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.resetQueries( - arg1Unreffed, - arg2Unreffed as ResetQueryFilters | undefined, - cloneDeepUnref(arg3) as ResetOptions, - ) - } - return super.resetQueries( - arg1Unreffed as ResetQueryFilters, - arg2Unreffed as ResetOptions, - ) + const filtersUnreffed = cloneDeepUnref(filters) as ResetQueryFilters + const optionsUnreffed = cloneDeepUnref(options) as ResetOptions + + return super.resetQueries(filtersUnreffed, optionsUnreffed) } cancelQueries( filters?: MaybeRefDeep, options?: MaybeRefDeep, - ): Promise - cancelQueries( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep, - options?: MaybeRefDeep, - ): Promise - cancelQueries( - arg1?: MaybeRefDeep, - arg2?: MaybeRefDeep, - arg3?: MaybeRefDeep, ): Promise { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.cancelQueries( - arg1Unreffed, - arg2Unreffed as QueryFilters | undefined, - cloneDeepUnref(arg3) as CancelOptions, - ) - } - return super.cancelQueries( - arg1Unreffed as QueryFilters, - arg2Unreffed as CancelOptions, - ) + const filtersUnreffed = cloneDeepUnref(filters) as QueryFilters + const optionsUnreffed = cloneDeepUnref(options) as CancelOptions + return super.cancelQueries(filtersUnreffed, optionsUnreffed) } invalidateQueries( filters?: MaybeRefDeep>, options?: MaybeRefDeep, - ): Promise - invalidateQueries( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep>, - options?: MaybeRefDeep, - ): Promise - invalidateQueries( - arg1?: MaybeRefDeep>, - arg2?: MaybeRefDeep | InvalidateOptions>, - arg3?: MaybeRefDeep, ): Promise { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.invalidateQueries( - arg1Unreffed, - arg2Unreffed as InvalidateQueryFilters | undefined, - cloneDeepUnref(arg3) as InvalidateOptions, - ) - } - return super.invalidateQueries( - arg1Unreffed as InvalidateQueryFilters, - arg2Unreffed as InvalidateOptions, - ) + const filtersUnreffed = cloneDeepUnref( + filters, + ) as InvalidateQueryFilters + const optionsUnreffed = cloneDeepUnref(options) as InvalidateOptions + + return super.invalidateQueries(filtersUnreffed, optionsUnreffed) } refetchQueries( filters?: MaybeRefDeep>, options?: MaybeRefDeep, - ): Promise - refetchQueries( - queryKey?: MaybeRefDeep, - filters?: MaybeRefDeep>, - options?: MaybeRefDeep, - ): Promise - refetchQueries( - arg1?: MaybeRefDeep>, - arg2?: MaybeRefDeep | RefetchOptions>, - arg3?: MaybeRefDeep, ): Promise { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.refetchQueries( - arg1Unreffed, - arg2Unreffed as RefetchQueryFilters | undefined, - cloneDeepUnref(arg3) as RefetchOptions, - ) - } - return super.refetchQueries( - arg1Unreffed as RefetchQueryFilters, - arg2Unreffed as RefetchOptions, - ) + const filtersUnreffed = cloneDeepUnref( + filters, + ) as RefetchQueryFilters + const optionsUnreffed = cloneDeepUnref(options) as RefetchOptions + + return super.refetchQueries(filtersUnreffed, optionsUnreffed) } fetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - options: MaybeRefDeep< - FetchQueryOptions - >, - ): Promise - fetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - options?: MaybeRefDeep< - FetchQueryOptions - >, - ): Promise - fetchQuery< - TQueryFnData = unknown, - TError = unknown, + TQueryFnData, + TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: MaybeRefDeep, - queryFn: QueryFunction, - options?: MaybeRefDeep< - FetchQueryOptions - >, + options: FetchQueryOptions, ): Promise fetchQuery< TQueryFnData, @@ -313,33 +155,18 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | MaybeRefDeep - | MaybeRefDeep>, - arg2?: - | QueryFunction - | MaybeRefDeep>, - arg3?: MaybeRefDeep< + options: MaybeRefDeep< FetchQueryOptions >, ): Promise { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.fetchQuery( - arg1Unreffed as TQueryKey, - arg2Unreffed as QueryFunction, - cloneDeepUnref(arg3) as FetchQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey - >, - ) - } - return super.fetchQuery( - arg1Unreffed as FetchQueryOptions, - ) + const optionsUnreffed = cloneDeepUnref(options) as FetchQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey + > + + return super.fetchQuery(optionsUnreffed) } prefetchQuery< @@ -348,20 +175,7 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: MaybeRefDeep< - FetchQueryOptions - >, - ): Promise - prefetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - options?: MaybeRefDeep< - FetchQueryOptions - >, + options: FetchQueryOptions, ): Promise prefetchQuery< TQueryFnData = unknown, @@ -369,33 +183,17 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: MaybeRefDeep, - queryFn: QueryFunction, - options?: MaybeRefDeep< - FetchQueryOptions - >, - ): Promise - prefetchQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - arg1: MaybeRefDeep< - TQueryKey | FetchQueryOptions - >, - arg2?: - | QueryFunction - | MaybeRefDeep>, - arg3?: MaybeRefDeep< + options: MaybeRefDeep< FetchQueryOptions >, ): Promise { - return super.prefetchQuery( - cloneDeepUnref(arg1) as any, - cloneDeepUnref(arg2) as any, - cloneDeepUnref(arg3) as any, - ) + const optionsUnreffed = cloneDeepUnref(options) as FetchQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey + > + return super.prefetchQuery(optionsUnreffed) } fetchInfiniteQuery< @@ -404,32 +202,7 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: MaybeRefDeep< - FetchInfiniteQueryOptions - >, - ): Promise> - fetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - options?: MaybeRefDeep< - FetchInfiniteQueryOptions - >, - ): Promise> - fetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - queryFn: QueryFunction, - options?: MaybeRefDeep< - FetchInfiniteQueryOptions - >, + options: FetchInfiniteQueryOptions, ): Promise> fetchInfiniteQuery< TQueryFnData, @@ -437,41 +210,15 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: MaybeRefDeep< - | TQueryKey - | FetchInfiniteQueryOptions - >, - arg2?: - | QueryFunction - | MaybeRefDeep< - FetchInfiniteQueryOptions - >, - arg3?: MaybeRefDeep< + options: MaybeRefDeep< FetchInfiniteQueryOptions >, ): Promise> { - const arg1Unreffed = cloneDeepUnref(arg1) - const arg2Unreffed = cloneDeepUnref(arg2) - if (isQueryKey(arg1Unreffed)) { - return super.fetchInfiniteQuery( - arg1Unreffed as TQueryKey, - arg2Unreffed as QueryFunction, - cloneDeepUnref(arg3) as FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey - >, - ) - } - return super.fetchInfiniteQuery( - arg1Unreffed as FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey - >, - ) + const optionsUnreffed = cloneDeepUnref( + options, + ) as FetchInfiniteQueryOptions + + return super.fetchInfiniteQuery(optionsUnreffed) } prefetchInfiniteQuery< @@ -480,32 +227,7 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: MaybeRefDeep< - FetchInfiniteQueryOptions - >, - ): Promise - prefetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - options?: MaybeRefDeep< - FetchInfiniteQueryOptions - >, - ): Promise - prefetchInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - >( - queryKey: MaybeRefDeep, - queryFn: QueryFunction, - options?: MaybeRefDeep< - FetchInfiniteQueryOptions - >, + options: FetchInfiniteQueryOptions, ): Promise prefetchInfiniteQuery< TQueryFnData, @@ -513,24 +235,14 @@ export class QueryClient extends QC { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: MaybeRefDeep< - | TQueryKey - | FetchInfiniteQueryOptions - >, - arg2?: - | QueryFunction - | MaybeRefDeep< - FetchInfiniteQueryOptions - >, - arg3?: MaybeRefDeep< + options: MaybeRefDeep< FetchInfiniteQueryOptions >, ): Promise { - return super.prefetchInfiniteQuery( - cloneDeepUnref(arg1) as any, - cloneDeepUnref(arg2) as any, - cloneDeepUnref(arg3) as any, - ) + const optionsUnreffed = cloneDeepUnref( + options, + ) as FetchInfiniteQueryOptions + return super.prefetchInfiniteQuery(optionsUnreffed) } setDefaultOptions(options: MaybeRefDeep): void { @@ -539,12 +251,16 @@ export class QueryClient extends QC { setQueryDefaults( queryKey: MaybeRefDeep, - options: MaybeRefDeep>, + options: MaybeRefDeep< + Omit, 'queryKey'> + >, ): void { - super.setQueryDefaults( - cloneDeepUnref(queryKey), - cloneDeepUnref(options) as any, - ) + const queryKeyUnreffed = cloneDeepUnref(queryKey) as QueryKey + const optionsUnreffed = cloneDeepUnref(options) as Omit< + QueryObserverOptions, + 'queryKey' + > + super.setQueryDefaults(queryKeyUnreffed, optionsUnreffed) } getQueryDefaults( diff --git a/packages/vue-query/src/useBaseQuery.ts b/packages/vue-query/src/useBaseQuery.ts index fb133f7074..1d3b287378 100644 --- a/packages/vue-query/src/useBaseQuery.ts +++ b/packages/vue-query/src/useBaseQuery.ts @@ -8,16 +8,15 @@ import { computed, unref, } from 'vue-demi' -import type { ToRefs, UnwrapRef } from 'vue-demi' +import type { ToRefs } from 'vue-demi' import type { QueryObserver, QueryKey, QueryObserverOptions, QueryObserverResult, - QueryFunction, } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' -import { updateState, isQueryKey, cloneDeepUnref } from './utils' +import { updateState, cloneDeepUnref } from './utils' import type { MaybeRef, WithQueryClientKey } from './types' import type { UseQueryOptions } from './useQuery' import type { UseInfiniteQueryOptions } from './useInfiniteQuery' @@ -46,15 +45,14 @@ export function useBaseQuery< TQueryKey extends QueryKey, >( Observer: typeof QueryObserver, - arg1: - | TQueryKey - | UseQueryOptionsGeneric, - arg2: - | QueryFunction> - | UseQueryOptionsGeneric = {}, - arg3: UseQueryOptionsGeneric = {}, + genericOptions: UseQueryOptionsGeneric< + TQueryFnData, + TError, + TData, + TQueryKey + >, ): UseQueryReturnType { - const options = computed(() => parseQueryArgs(arg1, arg2, arg3)) + const options = computed(() => unrefQueryArgs(genericOptions)) const queryClient = options.value.queryClient ?? useQueryClient(options.value.queryClientKey) @@ -136,41 +134,20 @@ export function useBaseQuery< } } -export function parseQueryArgs< +export function unrefQueryArgs< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | MaybeRef - | MaybeRef>, - arg2: - | MaybeRef>> - | MaybeRef< - UseQueryOptionsGeneric - > = {}, - arg3: MaybeRef< + arg1: MaybeRef< UseQueryOptionsGeneric - > = {}, + >, ): WithQueryClientKey< QueryObserverOptions > { - const plainArg1 = unref(arg1) - const plainArg2 = unref(arg2) - const plainArg3 = unref(arg3) - - let options = plainArg1 - - if (!isQueryKey(plainArg1)) { - options = plainArg1 - } else if (typeof plainArg2 === 'function') { - options = { ...plainArg3, queryKey: plainArg1, queryFn: plainArg2 } - } else { - options = { ...plainArg2, queryKey: plainArg1 } - } - + const options = unref(arg1) return cloneDeepUnref(options) as WithQueryClientKey< QueryObserverOptions > diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index ed4b1fd9c5..ea590fbf7c 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -1,8 +1,7 @@ import { InfiniteQueryObserver } from '@tanstack/query-core' -import type { UnwrapRef } from 'vue-demi' import type { QueryObserver, - QueryFunction, + WithRequired, QueryKey, InfiniteQueryObserverResult, } from '@tanstack/query-core' @@ -21,14 +20,17 @@ export type UseInfiniteQueryOptions< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = WithQueryClientKey< - VueInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - > +> = WithRequired< + WithQueryClientKey< + VueInfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + > + >, + 'queryKey' > type InfiniteQueryReturnType = UseQueryReturnType< @@ -48,61 +50,17 @@ export type UseInfiniteQueryReturnType = DistributiveOmit< refetch: InfiniteQueryObserverResult['refetch'] } -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: UseInfiniteQueryOptions, -): UseInfiniteQueryReturnType - -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseInfiniteQueryOptions, - 'queryKey' - >, -): UseInfiniteQueryReturnType - -export function useInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - UseInfiniteQueryOptions, - 'queryKey' | 'queryFn' - >, -): UseInfiniteQueryReturnType - export function useInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: - | TQueryKey - | UseInfiniteQueryOptions, - arg2?: - | QueryFunction> - | UseInfiniteQueryOptions, - arg3?: UseInfiniteQueryOptions, + options: UseInfiniteQueryOptions, ): UseInfiniteQueryReturnType { const result = useBaseQuery( InfiniteQueryObserver as typeof QueryObserver, - arg1, - arg2, - arg3, + options, ) as InfiniteQueryReturnType return { ...result, diff --git a/packages/vue-query/src/useIsFetching.ts b/packages/vue-query/src/useIsFetching.ts index c19e1a8ed1..41cbfb0a28 100644 --- a/packages/vue-query/src/useIsFetching.ts +++ b/packages/vue-query/src/useIsFetching.ts @@ -1,23 +1,14 @@ import { computed, unref, onScopeDispose, ref, watch } from 'vue-demi' import type { Ref } from 'vue-demi' -import type { QueryKey, QueryFilters as QF } from '@tanstack/query-core' - +import type { QueryFilters as QF } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' -import { cloneDeepUnref, isQueryKey } from './utils' -import type { MaybeRef, MaybeRefDeep, WithQueryClientKey } from './types' +import { cloneDeepUnref } from './utils' +import type { MaybeRefDeep, WithQueryClientKey } from './types' export type QueryFilters = MaybeRefDeep> -export function useIsFetching(filters?: QueryFilters): Ref -export function useIsFetching( - queryKey?: MaybeRef, - filters?: Omit, -): Ref -export function useIsFetching( - arg1?: MaybeRef | QueryFilters, - arg2?: Omit, -): Ref { - const filters = computed(() => parseFilterArgs(arg1, arg2)) +export function useIsFetching(fetchingFilters: QueryFilters = {}): Ref { + const filters = computed(() => unrefFilterArgs(fetchingFilters)) const queryClient = filters.value.queryClient ?? useQueryClient(filters.value.queryClientKey) @@ -42,21 +33,7 @@ export function useIsFetching( return isFetching } -export function parseFilterArgs( - arg1?: MaybeRef | QueryFilters, - arg2: QueryFilters = {}, -) { - const plainArg1 = unref(arg1) - const plainArg2 = unref(arg2) - - let options = plainArg1 - - if (isQueryKey(plainArg1)) { - options = { ...plainArg2, queryKey: plainArg1 } - } else { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - options = plainArg1 || {} - } - +export function unrefFilterArgs(arg: QueryFilters) { + const options = unref(arg) return cloneDeepUnref(options) as WithQueryClientKey } diff --git a/packages/vue-query/src/useIsMutating.ts b/packages/vue-query/src/useIsMutating.ts index 1c148f922b..66e94cd130 100644 --- a/packages/vue-query/src/useIsMutating.ts +++ b/packages/vue-query/src/useIsMutating.ts @@ -1,23 +1,17 @@ import { computed, unref, onScopeDispose, ref, watch } from 'vue-demi' import type { Ref } from 'vue-demi' -import type { MutationKey, MutationFilters as MF } from '@tanstack/query-core' +import type { MutationFilters as MF } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' -import { cloneDeepUnref, isQueryKey } from './utils' -import type { MaybeRef, MaybeRefDeep, WithQueryClientKey } from './types' +import { cloneDeepUnref } from './utils' +import type { MaybeRefDeep, WithQueryClientKey } from './types' export type MutationFilters = MaybeRefDeep> -export function useIsMutating(filters?: MutationFilters): Ref export function useIsMutating( - mutationKey?: MaybeRef, - filters?: Omit, -): Ref -export function useIsMutating( - arg1?: MaybeRef | MutationFilters, - arg2?: Omit, + mutationFilters: MutationFilters = {}, ): Ref { - const filters = computed(() => parseFilterArgs(arg1, arg2)) + const filters = computed(() => unrefFilterArgs(mutationFilters)) const queryClient = filters.value.queryClient ?? useQueryClient(filters.value.queryClientKey) @@ -42,21 +36,7 @@ export function useIsMutating( return isMutating } -export function parseFilterArgs( - arg1?: MaybeRef | MutationFilters, - arg2: MutationFilters = {}, -) { - const plainArg1 = unref(arg1) - const plainArg2 = unref(arg2) - - let options = plainArg1 - - if (isQueryKey(plainArg1)) { - options = { ...plainArg2, mutationKey: plainArg1 } - } else { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - options = plainArg1 || {} - } - +export function unrefFilterArgs(arg: MutationFilters) { + const options = unref(arg) return cloneDeepUnref(options) as WithQueryClientKey } diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index bf45e2c176..1337b516d2 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -10,8 +10,6 @@ import { import type { ToRefs } from 'vue-demi' import type { MutateOptions, - MutationFunction, - MutationKey, MutateFunction, MutationObserverResult, MutationObserverOptions, @@ -23,7 +21,7 @@ import type { DistributiveOmit, } from './types' import { MutationObserver } from '@tanstack/query-core' -import { cloneDeepUnref, updateState, isMutationKey } from './utils' +import { cloneDeepUnref, updateState } from './utils' import { useQueryClient } from './useQueryClient' type MutationResult = DistributiveOmit< @@ -79,72 +77,12 @@ export function useMutation< TVariables = void, TContext = unknown, >( - options: MaybeRef< - VueMutationObserverOptions - >, -): UseMutationReturnType -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationFn: MaybeRef>, - options?: MaybeRef< - Omit< - VueMutationObserverOptions, - 'mutationFn' - > - >, -): UseMutationReturnType -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MaybeRef, - options?: MaybeRef< - Omit< - VueMutationObserverOptions, - 'mutationKey' - > - >, -): UseMutationReturnType -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - mutationKey: MaybeRef, - mutationFn?: MaybeRef>, - options?: MaybeRef< - Omit< - VueMutationObserverOptions, - 'mutationKey' | 'mutationFn' - > - >, -): UseMutationReturnType -export function useMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown, ->( - arg1: - | MaybeRef - | MaybeRef> - | MaybeRef>, - arg2?: - | MaybeRef> - | MaybeRef>, - arg3?: MaybeRef< + mutationOptions: MaybeRef< VueMutationObserverOptions >, ): UseMutationReturnType { const options = computed(() => { - return parseMutationArgs(arg1, arg2, arg3) + return unrefMutationArgs(mutationOptions) }) const queryClient = options.value.queryClient ?? useQueryClient(options.value.queryClientKey) @@ -191,40 +129,19 @@ export function useMutation< } } -export function parseMutationArgs< +export function unrefMutationArgs< TData = unknown, TError = unknown, TVariables = void, TContext = unknown, >( - arg1: - | MaybeRef - | MaybeRef> - | MaybeRef>, - arg2?: - | MaybeRef> - | MaybeRef>, - arg3?: MaybeRef< + arg: MaybeRef< VueMutationObserverOptions >, ): WithQueryClientKey< MutationObserverOptions > { - const plainArg1 = unref(arg1) - const plainArg2 = unref(arg2) - let options = plainArg1 - if (isMutationKey(plainArg1)) { - if (typeof plainArg2 === 'function') { - const plainArg3 = unref(arg3) - options = { ...plainArg3, mutationKey: plainArg1, mutationFn: plainArg2 } - } else { - options = { ...plainArg2, mutationKey: plainArg1 } - } - } - - if (typeof plainArg1 === 'function') { - options = { ...plainArg2, mutationFn: plainArg1 } - } + const options = unref(arg) return cloneDeepUnref(options) as UseMutationOptions< TData, diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index fa11af87de..42dff8aa62 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -1,10 +1,10 @@ -import type { ToRefs, UnwrapRef } from 'vue-demi' +import type { ToRefs } from 'vue-demi' import { QueryObserver } from '@tanstack/query-core' import type { - QueryFunction, QueryKey, QueryObserverResult, DefinedQueryObserverResult, + WithRequired, } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' import type { UseQueryReturnType as UQRT } from './useBaseQuery' @@ -34,81 +34,36 @@ export type UseQueryOptions< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = WithQueryClientKey< - VueQueryObserverOptions +> = WithRequired< + WithQueryClientKey< + VueQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + > + >, + 'queryKey' > -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit< - UseQueryOptions, - 'initialData' - > & { initialData?: () => undefined }, -): UseQueryReturnType - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit< - UseQueryOptions, - 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, -): UseQueryDefinedReturnType - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: UseQueryOptions, -): UseQueryReturnType - -export function useQuery< +type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'initialData' - > & { initialData?: () => undefined }, -): UseQueryReturnType - -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, -): UseQueryDefinedReturnType +> = UseQueryOptions & { + initialData?: undefined +} -export function useQuery< +type DefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit< - UseQueryOptions, - 'queryKey' - >, -): UseQueryReturnType +> = UseQueryOptions & { + initialData: TQueryFnData | (() => TQueryFnData) +} export function useQuery< TQueryFnData = unknown, @@ -116,12 +71,7 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData?: () => undefined }, + options: UndefinedInitialDataOptions, ): UseQueryReturnType export function useQuery< @@ -130,43 +80,20 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' | 'initialData' - > & { initialData: TQueryFnData | (() => TQueryFnData) }, + options: DefinedInitialDataOptions, ): UseQueryDefinedReturnType -export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction>, - options?: Omit< - UseQueryOptions, - 'queryKey' | 'queryFn' - >, -): UseQueryReturnType - export function useQuery< TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: TQueryKey | UseQueryOptions, - arg2?: - | QueryFunction> - | UseQueryOptions, - arg3?: UseQueryOptions, + options: UseQueryOptions, ): | UseQueryReturnType | UseQueryDefinedReturnType { - const result = useBaseQuery(QueryObserver, arg1, arg2, arg3) + const result = useBaseQuery(QueryObserver, options) return { ...result, From b0d3c62070e8960ced79e6686c6c2f0154582a55 Mon Sep 17 00:00:00 2001 From: Aryan Deora Date: Tue, 3 Jan 2023 12:05:03 -0500 Subject: [PATCH 006/314] feat: (solid-query): Horizontal upgrade v5 (#4736) * feat(solid-query): Horizontal upgrade v5 * fix(solid-query): Correct linting errors * fix(solid-query): Include correct side effects Adds `setBatchUpdatesFn.ts` to the list of sideEffects. --- docs/solid/overview.md | 96 +- .../solid/basic-graphql-request/src/index.tsx | 14 +- examples/solid/basic-typescript/src/index.tsx | 14 +- .../default-query-function/src/index.tsx | 11 +- examples/solid/simple/.eslintrc | 3 +- examples/solid/simple/src/index.tsx | 6 +- .../src/__tests__/useQueries.test.tsx | 2 +- packages/solid-query/package.json | 6 +- .../__tests__/QueryClientProvider.test.tsx | 74 +- .../__tests__/createInfiniteQuery.test.tsx | 120 +- .../src/__tests__/createMutation.test.tsx | 106 +- .../src/__tests__/createQueries.test.tsx | 412 +++---- .../src/__tests__/createQuery.test.tsx | 1052 +++++++---------- .../src/__tests__/createQuery.types.test.tsx | 50 +- .../src/__tests__/suspense.test.tsx | 145 +-- .../src/__tests__/transition.test.tsx | 4 +- .../src/__tests__/useIsFetching.test.tsx | 60 +- .../src/__tests__/useIsMutating.test.tsx | 60 +- packages/solid-query/src/__tests__/utils.tsx | 6 +- packages/solid-query/src/createBaseQuery.ts | 80 +- .../solid-query/src/createInfiniteQuery.ts | 31 +- packages/solid-query/src/createMutation.ts | 10 +- packages/solid-query/src/createQueries.ts | 72 +- packages/solid-query/src/createQuery.ts | 55 +- .../solid-query/src/{index.tsx => index.ts} | 7 +- packages/solid-query/src/setBatchUpdatesFn.ts | 4 + packages/solid-query/src/types.ts | 69 +- packages/solid-query/src/useIsFetching.ts | 45 +- packages/solid-query/src/useIsMutating.ts | 28 +- packages/solid-query/src/utils.ts | 46 - rollup.config.ts | 8 +- 31 files changed, 1158 insertions(+), 1538 deletions(-) rename packages/solid-query/src/{index.tsx => index.ts} (92%) create mode 100644 packages/solid-query/src/setBatchUpdatesFn.ts diff --git a/docs/solid/overview.md b/docs/solid/overview.md index ecb6bb20a0..905c616a33 100644 --- a/docs/solid/overview.md +++ b/docs/solid/overview.md @@ -1,23 +1,27 @@ --- id: overview -title: Solid Query +title: Solid Query --- -The `@tanstack/solid-query` package provides a 1st-class API for using TanStack Query with SolidJS. +The `@tanstack/solid-query` package provides a 1st-class API for using TanStack Query with SolidJS. ## Example ```tsx -import { QueryClient, QueryClientProvider, createQuery } from '@tanstack/solid-query' +import { + QueryClient, + QueryClientProvider, + createQuery, +} from '@tanstack/solid-query' import { Switch, Match, For } from 'solid-js' const queryClient = new QueryClient() function Example() { - const query = createQuery({ - queryKey: () => ['todos'], - queryFn: fetchTodos - }) + const query = createQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) return (
    @@ -29,9 +33,7 @@ function Example() {

    Error: {query.error.message}

    - - {(todo) =>

    {todo.title}

    } -
    + {(todo) =>

    {todo.title}

    }
    @@ -45,7 +47,6 @@ function App() { ) } - ``` ## Available Functions @@ -62,27 +63,24 @@ Solid Query offers useful primitives and functions that will make managing serve - `QueryClient` - `QueryClientProvider` - - - ## Important Differences between Solid Query & React Query -Solid Query offers an API similar to React Query, but there are some key differences to be mindful of. +Solid Query offers an API similar to React Query, but there are some key differences to be mindful of. -- To maintain their reactivity, Query keys need to be wrapped inside a function while using `createQuery`, `createQueries`, `createInfiniteQuery` and `useIsFetching`. +- Arguments to `solid-query` primitives (like `createQuery`, `createMutation`, `useIsFetching`) listed above are functions, so that they can be tracked in a reactive scope. ```tsx // ❌ react version useQuery({ - queryKey: ["todos", todo], - queryFn: fetchTodos + queryKey: ['todos', todo], + queryFn: fetchTodos, }) // ✅ solid version -createQuery({ - queryKey: () => ["todos", todo()], - queryFn: fetchTodos -}) +createQuery(() => ({ + queryKey: ['todos', todo], + queryFn: fetchTodos, +})) ``` - Suspense works for queries out of the box if you access the query data inside a `` boundary. @@ -91,18 +89,17 @@ createQuery({ import { For, Suspense } from 'solid-js' function Example() { - const query = createQuery({ - queryKey: () => ['todos'], - queryFn: fetchTodos - } - ) + const query = createQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) return (
    - {/* ✅ Will trigger loading fallback, data accessed in a suspense context. */} - + {/* ✅ Will trigger loading fallback, data accessed in a suspense boundary. */} + {(todo) =>
    {todo.title}
    }
    - {/* ❌ Will not trigger loading fallback, data not accessed in a suspense context. */} + {/* ❌ Will not trigger loading fallback, data not accessed in a suspense boundary. */} {(todo) =>
    {todo.title}
    }
    ) @@ -112,7 +109,11 @@ function Example() { - Solid Query primitives (`createX`) do not support destructuring. The return value from these functions is a store, and their properties are only tracked in a reactive context. ```tsx -import { QueryClient, QueryClientProvider, createQuery } from '@tanstack/solid-query' +import { + QueryClient, + QueryClientProvider, + createQuery, +} from '@tanstack/solid-query' import { Match, Switch } from 'solid-js' const queryClient = new QueryClient() @@ -162,7 +163,7 @@ function Example() { } ``` -- If you want options to be reactive you need to pass them using object getter syntax. This may look strange at first but it leads to more idiomatic solid code. +- Signals and store values can be passed in directly to function arguments. Solid Query will update the query `store` automatically. ```tsx import { @@ -176,17 +177,21 @@ const queryClient = new QueryClient() function Example() { const [enabled, setEnabled] = createSignal(false) - const query = createQuery({ - queryKey: () => ['todos'], + const [todo, setTodo] = createSignal(0) + + // ✅ passing a signal directly is safe and observers update + // automatically when the value of a signal changes + const todosQuery = createQuery(() => ({ + queryKey: ['todos'], queryFn: fetchTodos, - // ❌ passing a signal directly is not reactive - // enabled: enabled(), + enabled: enabled(), + })) - // ✅ passing a function that returns a signal is reactive - get enabled() { - return enabled() - }, - }) + const todoDetailsQuery = createQuery(() => ({ + queryKey: ['todo', todo()], + queryFn: fetchTodo, + enabled: todo() > 0, + })) return (
    @@ -199,7 +204,9 @@ function Example() { - {(todo) =>

    {todo.title}

    } + {(todo) => ( + + )}
    @@ -217,6 +224,7 @@ function App() { } ``` -- Errors can be caught and reset using SolidJS' native `ErrorBoundary` component. `QueryErrorResetBoundary` is not needed with Solid Query +- Errors can be caught and reset using SolidJS' native `ErrorBoundary` component. + Set `throwErrors` or the `suspense` option to `true` to make sure errors are thrown to the `ErrorBoundary` -- Since Property tracking is handled through Solid's fine grained reactivity, options like `notifyOnChangeProps` are not needed \ No newline at end of file +- Since Property tracking is handled through Solid's fine grained reactivity, options like `notifyOnChangeProps` are not needed diff --git a/examples/solid/basic-graphql-request/src/index.tsx b/examples/solid/basic-graphql-request/src/index.tsx index 6a1d6654da..b8f8eb3aba 100644 --- a/examples/solid/basic-graphql-request/src/index.tsx +++ b/examples/solid/basic-graphql-request/src/index.tsx @@ -3,7 +3,6 @@ import { createQuery, QueryClient, QueryClientProvider, - useQueryClient, } from '@tanstack/solid-query' import type { Accessor, Setter } from 'solid-js' import { createSignal, For, Match, Switch } from 'solid-js' @@ -39,8 +38,8 @@ function App() { } function createPosts() { - return createQuery({ - queryKey: () => ['posts'], + return createQuery(() => ({ + queryKey: ['posts'], queryFn: async () => { const { posts: { data }, @@ -59,11 +58,10 @@ function createPosts() { ) return data }, - }) + })) } function Posts(props: { setPostId: Setter }) { - const queryClient = useQueryClient() const state = createPosts() return ( @@ -111,8 +109,8 @@ function Posts(props: { setPostId: Setter }) { } function createPost(postId: Accessor) { - return createQuery({ - queryKey: () => ['post', postId()], + return createQuery(() => ({ + queryKey: ['post', postId()], queryFn: async (context) => { const { post } = await request( endpoint, @@ -130,7 +128,7 @@ function createPost(postId: Accessor) { return post }, enabled: !!postId, - }) + })) } function Post(props: { postId: number; setPostId: Setter }) { diff --git a/examples/solid/basic-typescript/src/index.tsx b/examples/solid/basic-typescript/src/index.tsx index 502ccaa7c7..d4b79e1005 100644 --- a/examples/solid/basic-typescript/src/index.tsx +++ b/examples/solid/basic-typescript/src/index.tsx @@ -3,7 +3,6 @@ import { createQuery, QueryClient, QueryClientProvider, - useQueryClient, } from '@tanstack/solid-query' import type { Component, Setter } from 'solid-js' import { createSignal, For, Match, Switch } from 'solid-js' @@ -24,8 +23,8 @@ type Post = { } function createPosts() { - return createQuery({ - queryKey: () => ['posts'], + return createQuery(() => ({ + queryKey: ['posts'], queryFn: async (): Promise> => { const response = await fetch( 'https://jsonplaceholder.typicode.com/posts', @@ -35,11 +34,10 @@ function createPosts() { ) return response.json() }, - }) + })) } function Posts(props: { setPostId: Setter }) { - const queryClient = useQueryClient() const state = createPosts() return ( @@ -97,11 +95,11 @@ const getPostById = async (id: number): Promise => { } function createPost(postId: number) { - return createQuery({ - queryKey: () => ['post', postId], + return createQuery(() => ({ + queryKey: ['post', postId], queryFn: () => getPostById(postId), enabled: !!postId, - }) + })) } function Post(props: { postId: number; setPostId: Setter }) { diff --git a/examples/solid/default-query-function/src/index.tsx b/examples/solid/default-query-function/src/index.tsx index d2af08cc2a..d70b15dfe1 100644 --- a/examples/solid/default-query-function/src/index.tsx +++ b/examples/solid/default-query-function/src/index.tsx @@ -4,7 +4,6 @@ import { createQuery, QueryClient, QueryClientProvider, - useQueryClient, } from '@tanstack/solid-query' import type { Setter } from 'solid-js' import { createSignal, For, Match, Show, Switch } from 'solid-js' @@ -53,10 +52,8 @@ function App() { } function Posts(props: { setPostId: Setter }) { - const queryClient = useQueryClient() - // All you have to do now is pass a key! - const state = createQuery({ queryKey: () => ['/posts'] }) + const state = createQuery(() => ({ queryKey: ['/posts'] })) return (
    @@ -104,10 +101,10 @@ function Posts(props: { setPostId: Setter }) { function Post(props: { postId: number; setPostId: Setter }) { // You can even leave out the queryFn and just go straight into options - const state = createQuery({ - queryKey: () => [`/posts/${props.postId}`], + const state = createQuery(() => ({ + queryKey: [`/posts/${props.postId}`], enabled: !!props.postId, - }) + })) return (
    diff --git a/examples/solid/simple/.eslintrc b/examples/solid/simple/.eslintrc index 137996aecd..d8c452e40c 100644 --- a/examples/solid/simple/.eslintrc +++ b/examples/solid/simple/.eslintrc @@ -6,6 +6,7 @@ }, "rules": { "react/react-in-jsx-scope": "off", - "jsx-a11y/accessible-emoji": "off" + "jsx-a11y/accessible-emoji": "off", + "@tanstack/query/prefer-query-object-syntax": "off" } } diff --git a/examples/solid/simple/src/index.tsx b/examples/solid/simple/src/index.tsx index 2bd61c6b5e..165e3941e5 100644 --- a/examples/solid/simple/src/index.tsx +++ b/examples/solid/simple/src/index.tsx @@ -18,8 +18,8 @@ export default function App() { } function Example() { - const state = createQuery({ - queryKey: () => ['repoData'], + const state = createQuery(() => ({ + queryKey: ['repoData'], queryFn: async () => { const response = await fetch( 'https://api.github.com/repos/tannerlinsley/react-query', @@ -29,7 +29,7 @@ function Example() { ) return response.json() }, - }) + })) return ( diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 9434d33a87..2dfd3373ec 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -244,7 +244,7 @@ describe('useQueries', () => { return { queryKey: [key, id], queryFn: async () => { - await sleep(5) + await sleep(10) return id * 5 }, keepPreviousData: true, diff --git a/packages/solid-query/package.json b/packages/solid-query/package.json index 7453968c94..84ab35eed7 100644 --- a/packages/solid-query/package.json +++ b/packages/solid-query/package.json @@ -16,7 +16,7 @@ "exports": { ".": { "types": "./build/lib/index.d.ts", - "solid": "./build/solid/index.jsx", + "solid": "./build/solid/index.js", "import": "./build/lib/index.mjs", "browser": "./build/lib/index.mjs", "require": "./build/lib/index.js", @@ -25,7 +25,9 @@ }, "./package.json": "./package.json" }, - "sideEffects": false, + "sideEffects": [ + "./src/setBatchUpdatesFn.ts" + ], "scripts": { "clean": "rm -rf ./build", "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index 5d6fe28280..5fde9d2751 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -4,7 +4,6 @@ import { queryKey } from './utils' import { QueryCache, QueryClient } from '@tanstack/query-core' import type { Context } from 'solid-js' import { createContext, useContext } from 'solid-js' -import { renderToString } from 'solid-js/web' import { createQuery, QueryClientProvider, useQueryClient } from '..' import { createQueryClient, sleep } from './utils' @@ -16,13 +15,13 @@ describe('QueryClientProvider', () => { const queryClient = createQueryClient({ queryCache }) function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) return (
    @@ -41,7 +40,7 @@ describe('QueryClientProvider', () => { return screen.getByText('test') }) - expect(queryCache.find({ queryKey: key() })).toBeDefined() + expect(queryCache.find({ queryKey: key })).toBeDefined() }) it('allows multiple caches to be partitioned', async () => { @@ -55,13 +54,13 @@ describe('QueryClientProvider', () => { const queryClient2 = createQueryClient({ queryCache: queryCache2 }) function Page1() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key1, queryFn: async () => { await sleep(10) return 'test1' }, - }) + })) return (
    @@ -70,13 +69,13 @@ describe('QueryClientProvider', () => { ) } function Page2() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key2, queryFn: async () => { await sleep(10) return 'test2' }, - }) + })) return (
    @@ -99,10 +98,10 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test1')) await waitFor(() => screen.getByText('test2')) - expect(queryCache1.find({ queryKey: key1() })).toBeDefined() - expect(queryCache1.find({ queryKey: key2() })).not.toBeDefined() - expect(queryCache2.find({ queryKey: key1() })).not.toBeDefined() - expect(queryCache2.find({ queryKey: key2() })).toBeDefined() + expect(queryCache1.find({ queryKey: key1 })).toBeDefined() + expect(queryCache1.find({ queryKey: key2 })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key1 })).not.toBeDefined() + expect(queryCache2.find({ queryKey: key2 })).toBeDefined() }) it("uses defaultOptions for queries when they don't provide their own config", async () => { @@ -119,13 +118,13 @@ describe('QueryClientProvider', () => { }) function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) return (
    @@ -142,10 +141,8 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test')) - expect(queryCache.find({ queryKey: key() })).toBeDefined() - expect(queryCache.find({ queryKey: key() })?.options.cacheTime).toBe( - Infinity, - ) + expect(queryCache.find({ queryKey: key })).toBeDefined() + expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) }) describe('with custom context', () => { @@ -167,20 +164,20 @@ describe('QueryClientProvider', () => { }) function Page() { - const queryOuter = createQuery({ + const queryOuter = createQuery(() => ({ queryKey: key, queryFn: async () => 'testOuter', context: contextOuter, - }) - const queryInner = createQuery({ + })) + const queryInner = createQuery(() => ({ queryKey: key, queryFn: async () => 'testInner', context: contextInner, - }) - const queryInnerInner = createQuery({ + })) + const queryInnerInner = createQuery(() => ({ queryKey: key, queryFn: async () => 'testInnerInner', - }) + })) return (
    @@ -255,34 +252,5 @@ describe('QueryClientProvider', () => { expect(queryClientFromHook).toEqual(queryClient) expect(queryClientFromWindow).toEqual(queryClient) }) - - it.skip('should not use window to get the context when contextSharing is true and window does not exist', () => { - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - - // Mock a non web browser environment - const windowSpy = jest - .spyOn(window, 'window', 'get') - .mockImplementation(undefined) - - let queryClientFromHook: QueryClient | undefined - - function Page() { - queryClientFromHook = useQueryClient() - return null - } - - // TODO(lukemurray): fails because renderToString never calls Page - // probably an SSR-testing issue we need to fix. - renderToString(() => ( - - - - )) - - expect(queryClientFromHook).toEqual(queryClient) - - windowSpy.mockRestore() - }) }) }) diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx index 1730585e35..1546c92db8 100644 --- a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -52,11 +52,11 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 0 }) => Number(pageParam), getNextPageParam: (lastPage) => lastPage + 1, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -145,7 +145,7 @@ describe('useInfiniteQuery', () => { function Page() { const start = 1 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = start }) => { if (pageParam === 2) { @@ -157,7 +157,7 @@ describe('useInfiniteQuery', () => { retry: 1, retryDelay: 10, getNextPageParam: (lastPage) => lastPage + 1, - }) + })) createEffect(() => { const fetchNextPage = state.fetchNextPage @@ -189,8 +189,8 @@ describe('useInfiniteQuery', () => { function Page() { const [order, setOrder] = createSignal('desc') - const state = createInfiniteQuery({ - queryKey: () => [key(), order()], + const state = createInfiniteQuery(() => ({ + queryKey: [key, order()], queryFn: async ({ pageParam = 0 }) => { await sleep(10) return `${pageParam}-${order()}` @@ -199,7 +199,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: () => 1, keepPreviousData: true, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -281,14 +281,14 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: () => ({ count: 1 }), select: (data) => ({ pages: data.pages.map((x) => `count: ${x.count}`), pageParams: data.pageParams, }), - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -321,7 +321,7 @@ describe('useInfiniteQuery', () => { let selectCalled = 0 function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: () => ({ count: 1 }), select: (data: InfiniteData<{ count: number }>) => { @@ -331,7 +331,7 @@ describe('useInfiniteQuery', () => { pageParams: data.pageParams, } }, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -363,7 +363,7 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 0 }) => { await sleep(10) @@ -375,7 +375,7 @@ describe('useInfiniteQuery', () => { pageParams: [...data.pageParams].reverse(), }), notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -428,7 +428,7 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = start }) => { await sleep(10) @@ -437,7 +437,7 @@ describe('useInfiniteQuery', () => { getPreviousPageParam: (firstPage) => firstPage - 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -505,13 +505,13 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 10 }) => { await sleep(10) return Number(pageParam) }, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -616,7 +616,7 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 10 }) => { await sleep(10) @@ -626,7 +626,7 @@ describe('useInfiniteQuery', () => { getPreviousPageParam: (firstPage) => firstPage - 1, getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -730,7 +730,7 @@ describe('useInfiniteQuery', () => { function Page() { let multiplier = 1 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 10 }) => { await sleep(10) @@ -739,7 +739,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -824,7 +824,7 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = start }) => { await sleep(50) @@ -833,7 +833,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -905,7 +905,7 @@ describe('useInfiniteQuery', () => { const abortListeners: jest.Mock[] = [] const fetchPage = jest.fn< Promise, - [QueryFunctionContext, number>] + [QueryFunctionContext] >(async ({ pageParam = start, signal }) => { if (signal) { const onAbort = jest.fn() @@ -920,11 +920,11 @@ describe('useInfiniteQuery', () => { }) function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, - }) + })) createEffect(() => { const { fetchNextPage } = state @@ -955,7 +955,7 @@ describe('useInfiniteQuery', () => { let callIndex = 0 const firstCtx = fetchPage.mock.calls[callIndex]![0] expect(firstCtx.pageParam).toBeUndefined() - expect(firstCtx.queryKey).toEqual(key()) + expect(firstCtx.queryKey).toEqual(key) expect(firstCtx.signal).toBeInstanceOf(AbortSignal) expect(firstCtx.signal?.aborted).toBe(false) expect(onAborts[callIndex]).not.toHaveBeenCalled() @@ -964,7 +964,7 @@ describe('useInfiniteQuery', () => { callIndex = 1 const secondCtx = fetchPage.mock.calls[callIndex]![0] expect(secondCtx.pageParam).toBe(11) - expect(secondCtx.queryKey).toEqual(key()) + expect(secondCtx.queryKey).toEqual(key) expect(secondCtx.signal).toBeInstanceOf(AbortSignal) expect(secondCtx.signal?.aborted).toBe(true) expect(onAborts[callIndex]).toHaveBeenCalledTimes(1) @@ -973,7 +973,7 @@ describe('useInfiniteQuery', () => { callIndex = 2 const thirdCtx = fetchPage.mock.calls[callIndex]![0] expect(thirdCtx.pageParam).toBe(11) - expect(thirdCtx.queryKey).toEqual(key()) + expect(thirdCtx.queryKey).toEqual(key) expect(thirdCtx.signal).toBeInstanceOf(AbortSignal) expect(thirdCtx.signal?.aborted).toBe(false) expect(onAborts[callIndex]).not.toHaveBeenCalled() @@ -987,7 +987,7 @@ describe('useInfiniteQuery', () => { const abortListeners: jest.Mock[] = [] const fetchPage = jest.fn< Promise, - [QueryFunctionContext, number>] + [QueryFunctionContext] >(async ({ pageParam = start, signal }) => { if (signal) { const onAbort = jest.fn() @@ -1002,11 +1002,11 @@ describe('useInfiniteQuery', () => { }) function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: fetchPage, getNextPageParam: (lastPage) => lastPage + 1, - }) + })) createEffect(() => { const { fetchNextPage } = state @@ -1037,7 +1037,7 @@ describe('useInfiniteQuery', () => { let callIndex = 0 const firstCtx = fetchPage.mock.calls[callIndex]![0] expect(firstCtx.pageParam).toBeUndefined() - expect(firstCtx.queryKey).toEqual(key()) + expect(firstCtx.queryKey).toEqual(key) expect(firstCtx.signal).toBeInstanceOf(AbortSignal) expect(firstCtx.signal?.aborted).toBe(false) expect(onAborts[callIndex]).not.toHaveBeenCalled() @@ -1046,7 +1046,7 @@ describe('useInfiniteQuery', () => { callIndex = 1 const secondCtx = fetchPage.mock.calls[callIndex]![0] expect(secondCtx.pageParam).toBe(11) - expect(secondCtx.queryKey).toEqual(key()) + expect(secondCtx.queryKey).toEqual(key) expect(secondCtx.signal).toBeInstanceOf(AbortSignal) expect(secondCtx.signal?.aborted).toBe(false) expect(onAborts[callIndex]).not.toHaveBeenCalled() @@ -1059,7 +1059,7 @@ describe('useInfiniteQuery', () => { function Page() { const start = 10 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = start }) => { await sleep(50) @@ -1068,7 +1068,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1116,7 +1116,7 @@ describe('useInfiniteQuery', () => { const initialData = { pages: [1, 2, 3, 4], pageParams: [0, 1, 2, 3] } function List() { - createInfiniteQuery({ + createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 0, signal: _ }) => { fetches++ @@ -1128,7 +1128,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (_, allPages) => { return allPages.length === 4 ? undefined : allPages.length }, - }) + })) return null } @@ -1154,7 +1154,7 @@ describe('useInfiniteQuery', () => { await sleep(300) expect(fetches).toBe(2) - expect(queryClient.getQueryState(key())).toMatchObject({ + expect(queryClient.getQueryState(key)).toMatchObject({ data: initialData, status: 'success', error: null, @@ -1166,7 +1166,7 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = 0 }) => { await sleep(10) @@ -1175,7 +1175,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1237,7 +1237,7 @@ describe('useInfiniteQuery', () => { function Page() { const [firstPage, setFirstPage] = createSignal(0) - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam = firstPage() }) => { await sleep(10) @@ -1246,7 +1246,7 @@ describe('useInfiniteQuery', () => { getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1255,7 +1255,7 @@ describe('useInfiniteQuery', () => { createEffect(() => { const { refetch } = state setActTimeout(() => { - queryClient.setQueryData(key(), { pages: [7, 8], pageParams: [7, 8] }) + queryClient.setQueryData(key, { pages: [7, 8], pageParams: [7, 8] }) setFirstPage(7) }, 20) @@ -1322,7 +1322,7 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: async ({ pageParam }): Promise => { await sleep(10) @@ -1332,7 +1332,7 @@ describe('useInfiniteQuery', () => { initialData: { pages: [1], pageParams: [1] }, getNextPageParam: (lastPage) => lastPage + 1, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1392,12 +1392,12 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 1 }) => Number(pageParam), getNextPageParam: () => undefined, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1436,13 +1436,13 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 10 }): number => pageParam, initialData: { pages: [10], pageParams: [undefined] }, getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1480,13 +1480,13 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 10 }): number => pageParam, initialData: { pages: [10], pageParams: [undefined] }, getNextPageParam: () => undefined, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1524,7 +1524,7 @@ describe('useInfiniteQuery', () => { const states: CreateInfiniteQueryResult[] = [] function Page() { - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 1 }) => Number(pageParam), @@ -1533,7 +1533,7 @@ describe('useInfiniteQuery', () => { pages: data.pages.map((x) => x.toString()), pageParams: data.pageParams, }), - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1586,13 +1586,13 @@ describe('useInfiniteQuery', () => { function Page() { let fetchCountRef = 0 - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 0 }) => fetchItemsWithLimit(pageParam, fetchCountRef++), getNextPageParam: (lastPage) => lastPage.nextId, - }) + })) return (
    @@ -1636,7 +1636,7 @@ describe('useInfiniteQuery', () => { // makes an actual network request // and calls invalidateQueries in an onSuccess items.splice(4, 1) - queryClient.invalidateQueries({ queryKey: key() }) + queryClient.invalidateQueries({ queryKey: key }) }} > Remove item @@ -1716,7 +1716,7 @@ describe('useInfiniteQuery', () => { let fetchCountRef = 0 const [isRemovedLastPage, setIsRemovedLastPage] = createSignal(false) - const state = createInfiniteQuery({ + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn: ({ pageParam = 0 }) => fetchItems( @@ -1726,7 +1726,7 @@ describe('useInfiniteQuery', () => { ), getNextPageParam: (lastPage) => lastPage.nextId, - }) + })) return (
    @@ -1854,7 +1854,7 @@ describe('useInfiniteQuery', () => { } function Page() { - const state = createInfiniteQuery({ queryKey: key, queryFn }) + const state = createInfiniteQuery(() => ({ queryKey: key, queryFn })) return (

    Status: {state.status}

    diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index 97c4d5725e..b13a03606d 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -30,9 +30,9 @@ describe('useMutation', () => { it('should be able to reset `data`', async () => { function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: () => Promise.resolve('mutation'), - }) + })) return (
    @@ -66,13 +66,13 @@ describe('useMutation', () => { it('should be able to reset `error`', async () => { function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: () => { const err = new Error('Expected mock error. All is well!') err.stack = '' return Promise.reject(err) }, - }) + })) return (
    @@ -114,7 +114,7 @@ describe('useMutation', () => { const onSettledMock = jest.fn() function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: (vars: { count: number }) => Promise.resolve(vars.count), onSuccess: (data) => { onSuccessMock(data) @@ -122,7 +122,7 @@ describe('useMutation', () => { onSettled: (data) => { onSettledMock(data) }, - }) + })) return (
    @@ -188,9 +188,9 @@ describe('useMutation', () => { }) function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: mutateFn, - }) + })) return (
    @@ -238,7 +238,7 @@ describe('useMutation', () => { const [count, setCount] = createSignal(0) function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: (vars: { count: number }) => { const error = new Error( `Expected mock error. All is well! ${vars.count}`, @@ -252,7 +252,7 @@ describe('useMutation', () => { onSettled: (_data, error) => { onSettledMock(error?.message) }, - }) + })) return (
    @@ -316,7 +316,7 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (text: string) => text, onSuccess: async () => { callbacks.push('useMutation.onSuccess') @@ -324,7 +324,7 @@ describe('useMutation', () => { onSettled: async () => { callbacks.push('useMutation.onSettled') }, - }) + })) createEffect(() => { const { mutateAsync } = mutation @@ -367,7 +367,7 @@ describe('useMutation', () => { const callbacks: string[] = [] function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => Promise.reject('oops'), onError: async () => { @@ -376,7 +376,7 @@ describe('useMutation', () => { onSettled: async () => { callbacks.push('useMutation.onSettled') }, - }) + })) createEffect(() => { const { mutateAsync } = mutation @@ -419,7 +419,7 @@ describe('useMutation', () => { it('should be able to use mutation defaults', async () => { const key = queryKey() - queryClient.setMutationDefaults(key(), { + queryClient.setMutationDefaults(key, { mutationFn: async (text: string) => { await sleep(10) return text @@ -429,9 +429,9 @@ describe('useMutation', () => { const states: CreateMutationResult[] = [] function Page() { - const mutation = createMutation({ - mutationKey: key(), - }) + const mutation = createMutation(() => ({ + mutationKey: key, + })) createRenderEffect(() => { states.push({ ...mutation }) @@ -465,14 +465,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: (_text: string) => { count++ return Promise.reject('oops') }, retry: 1, retryDelay: 5, - }) + })) createEffect(() => { const { mutate } = mutation @@ -501,14 +501,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: (_text: string) => { count++ return Promise.reject(new Error('oops')) }, retry: 1, retryDelay: 5, - }) + })) return (
    @@ -568,14 +568,14 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { count++ await sleep(10) return count }, onMutate, - }) + })) return (
    @@ -620,13 +620,13 @@ describe('useMutation', () => { const states: Array = [] function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - }) + })) createRenderEffect(() => { states.push(`${mutation.status}, ${mutation.isPaused}`) @@ -674,7 +674,7 @@ describe('useMutation', () => { const states: CreateMutationResult[] = [] function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { await sleep(1) count++ @@ -683,7 +683,7 @@ describe('useMutation', () => { retry: 1, retryDelay: 5, networkMode: 'offlineFirst', - }) + })) createRenderEffect(() => { states.push({ ...mutation }) @@ -758,7 +758,7 @@ describe('useMutation', () => { it('should not change state if unmounted', async () => { function Mutates() { - const mutation = createMutation({ mutationFn: () => sleep(10) }) + const mutation = createMutation(() => ({ mutationFn: () => sleep(10) })) return } function Page() { @@ -782,14 +782,14 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is set to true', async () => { function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: () => { const err = new Error('Expected mock error. All is well!') err.stack = '' return Promise.reject(err) }, throwErrors: true, - }) + })) return (
    @@ -822,7 +822,7 @@ describe('useMutation', () => { it('should be able to throw an error when throwErrors is a function that returns true', async () => { let boundary = false function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: () => { const err = new Error('mock error') err.stack = '' @@ -832,7 +832,7 @@ describe('useMutation', () => { boundary = !boundary return !boundary }, - }) + })) return (
    @@ -888,16 +888,16 @@ describe('useMutation', () => { const metaErrorMessage = 'mutation failed' function Page() { - const mutationSucceed = createMutation({ + const mutationSucceed = createMutation(() => ({ mutationFn: async () => '', meta: { metaSuccessMessage }, - }) - const mutationError = createMutation({ + })) + const mutationError = createMutation(() => ({ mutationFn: async () => { throw new Error('') }, meta: { metaErrorMessage }, - }) + })) return (
    @@ -948,17 +948,17 @@ describe('useMutation', () => { } function Component() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { count++ await sleep(10) return count }, - mutationKey: mutationKey(), + mutationKey: mutationKey, cacheTime: 0, onSuccess, onSettled, - }) + })) return (
    @@ -993,7 +993,7 @@ describe('useMutation', () => { await waitFor(() => { expect( - queryClient.getMutationCache().findAll({ mutationKey: mutationKey() }), + queryClient.getMutationCache().findAll({ mutationKey: mutationKey }), ).toHaveLength(0) }) @@ -1010,10 +1010,10 @@ describe('useMutation', () => { const context = createContext(undefined) function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: () => Promise.resolve('mutation'), context, - }) + })) return (
    @@ -1049,9 +1049,9 @@ describe('useMutation', () => { const context = createContext(undefined) function Page() { - const { data = '' } = createMutation({ + const { data = '' } = createMutation(() => ({ mutationFn: () => Promise.resolve('mutation'), - }) + })) return (
    @@ -1081,7 +1081,7 @@ describe('useMutation', () => { let count = 0 function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { count++ await sleep(10) @@ -1089,7 +1089,7 @@ describe('useMutation', () => { }, onSuccess, onSettled, - }) + })) return (
    @@ -1143,14 +1143,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { await sleep(10) return 'result' }, onSuccess: () => Promise.reject(error), onError, - }) + })) return (
    @@ -1180,13 +1180,13 @@ describe('useMutation', () => { const mutateFnError = new Error('mutateFnError') function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, onError: () => Promise.reject(error), - }) + })) return (
    @@ -1219,14 +1219,14 @@ describe('useMutation', () => { const onError = jest.fn() function Page() { - const mutation = createMutation({ + const mutation = createMutation(() => ({ mutationFn: async (_text: string) => { await sleep(10) throw mutateFnError }, onSettled: () => Promise.reject(error), onError, - }) + })) return (
    diff --git a/packages/solid-query/src/__tests__/createQueries.test.tsx b/packages/solid-query/src/__tests__/createQueries.test.tsx index 836943a968..a451068a6f 100644 --- a/packages/solid-query/src/__tests__/createQueries.test.tsx +++ b/packages/solid-query/src/__tests__/createQueries.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from 'solid-testing-library' import * as QueriesObserverModule from '../../../query-core/src/queriesObserver' -import type { QueryFunctionContext } from '@tanstack/query-core' +import type { QueryFunctionContext, QueryKey } from '@tanstack/query-core' import { createContext, createMemo, @@ -11,12 +11,11 @@ import { ErrorBoundary, } from 'solid-js' import type { - CreateQueryOptions, CreateQueryResult, QueryClient, QueryFunction, QueryObserverResult, - SolidQueryKey, + SolidQueryOptions, } from '..' import { createQueries, @@ -42,7 +41,7 @@ describe('useQueries', () => { const results: CreateQueryResult[][] = [] function Page() { - const result = createQueries({ + const result = createQueries(() => ({ queries: [ { queryKey: key1, @@ -59,7 +58,7 @@ describe('useQueries', () => { }, }, ], - }) + })) createRenderEffect(() => { results.push([...result]) @@ -96,10 +95,10 @@ describe('useQueries', () => { function Page() { const [count, setCount] = createSignal(1) - const result = createQueries({ + const result = createQueries(() => ({ queries: [ { - queryKey: () => [key1(), count()], + queryKey: [key1, count()], keepPreviousData: true, queryFn: async () => { await sleep(10) @@ -107,7 +106,7 @@ describe('useQueries', () => { }, }, { - queryKey: () => [key2(), count()], + queryKey: [key2, count()], keepPreviousData: true, queryFn: async () => { await sleep(35) @@ -115,7 +114,7 @@ describe('useQueries', () => { }, }, ], - }) + })) createRenderEffect(() => { states.push([...result]) @@ -159,19 +158,16 @@ describe('useQueries', () => { function Page() { const [count, setCount] = createSignal(2) - const result = createQueries({ - // TODO(lukemurray): reactive queries doesn't appear to work - get queries() { - return Array.from({ length: count() }, (_, i) => ({ - queryKey: () => [key(), count(), i + 1], - keepPreviousData: true, - queryFn: async () => { - await sleep(35 * (i + 1)) - return (i + 1) * count() * 2 - }, - })) - }, - }) + const result = createQueries(() => ({ + queries: Array.from({ length: count() }, (_, i) => ({ + queryKey: [key, count(), i + 1], + keepPreviousData: true, + queryFn: async () => { + await sleep(35 * (i + 1)) + return (i + 1) * count() * 2 + }, + })), + })) createRenderEffect(() => { states.push([...result]) @@ -216,10 +212,10 @@ describe('useQueries', () => { const [series2, setSeries2] = createSignal(2) const ids = [series1, series2] - const result = createQueries({ + const result = createQueries(() => ({ queries: ids.map((id) => { return { - queryKey: () => [key(), id()], + queryKey: [key, id()], queryFn: async () => { await sleep(5) return id() * 5 @@ -227,7 +223,7 @@ describe('useQueries', () => { keepPreviousData: true, } }), - }) + })) createRenderEffect(() => { states.push([...result]) @@ -277,21 +273,18 @@ describe('useQueries', () => { const [enableId1, setEnableId1] = createSignal(true) const ids = createMemo(() => (enableId1() ? [1, 2] : [2])) - const result = createQueries({ - // TODO(lukemurray): same issue queries should be reactive - get queries() { - return ids().map((id) => { - return { - queryKey: () => [key(), id], - queryFn: async () => { - await sleep(5) - return id * 5 - }, - keepPreviousData: true, - } - }) - }, - }) + const result = createQueries(() => ({ + queries: ids().map((id) => { + return { + queryKey: [key, id], + queryFn: async () => { + await sleep(5) + return id * 5 + }, + keepPreviousData: true, + } + }), + })) createRenderEffect(() => { states.push([...result]) @@ -331,7 +324,7 @@ describe('useQueries', () => { await waitFor(() => screen.getByText('data1: 5 data2: 10')) await waitFor(() => screen.getByText('isFetching: false')) - await waitFor(() => expect(states.length).toBe(5)) + await waitFor(() => expect(states.length).toBe(6)) expect(states[0]).toMatchObject([ { @@ -349,16 +342,25 @@ describe('useQueries', () => { ]) expect(states[1]).toMatchObject([ { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + { + status: 'loading', + data: undefined, + isPreviousData: false, + isFetching: true, + }, ]) expect(states[2]).toMatchObject([ + { status: 'success', data: 5, isPreviousData: false, isFetching: false }, { status: 'success', data: 10, isPreviousData: false, isFetching: false }, ]) expect(states[3]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: true }, { status: 'success', data: 10, isPreviousData: false, isFetching: false }, ]) expect(states[4]).toMatchObject([ + { status: 'success', data: 5, isPreviousData: false, isFetching: true }, + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + expect(states[5]).toMatchObject([ { status: 'success', data: 5, isPreviousData: false, isFetching: false }, { status: 'success', data: 10, isPreviousData: false, isFetching: false }, ]) @@ -372,22 +374,24 @@ describe('useQueries', () => { // @ts-expect-error (Page component is not rendered) // eslint-disable-next-line function Page() { - const result1 = createQueries<[[number], [string], [string[], boolean]]>({ - queries: [ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - }, - ], - }) + const result1 = createQueries<[[number], [string], [string[], boolean]]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }), + ) expectType>(result1[0]) expectType>(result1[1]) expectType>(result1[2]) @@ -399,7 +403,7 @@ describe('useQueries', () => { // TData (3rd element) takes precedence over TQueryFnData (1st element) const result2 = createQueries< [[string, unknown, string], [string, unknown, number]] - >({ + >(() => ({ queries: [ { queryKey: key1, @@ -420,65 +424,65 @@ describe('useQueries', () => { }, }, ], - }) + })) expectType>(result2[0]) expectType>(result2[1]) expectType(result2[0].data) expectType(result2[1].data) // types should be enforced - createQueries<[[string, unknown, string], [string, boolean, number]]>({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectType(a) - expectTypeNotAny(a) - return a.toLowerCase() - }, - onSuccess: (a) => { - expectType(a) - expectTypeNotAny(a) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectType(a) - expectTypeNotAny(a) - return parseInt(a) - }, - onSuccess: (a) => { - expectType(a) - expectTypeNotAny(a) + createQueries<[[string, unknown, string], [string, boolean, number]]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, }, - onError: (e) => { - expectType(e) - expectTypeNotAny(e) + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + onError: (e) => { + expectType(e) + expectTypeNotAny(e) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - ], - }) + ], + }), + ) // field names should be enforced - createQueries<[[string]]>({ + createQueries<[[string]]>(() => ({ queries: [ { queryKey: key1, queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], }, ], - }) + })) } }) @@ -496,7 +500,7 @@ describe('useQueries', () => { { queryFnData: string }, { queryFnData: string[]; error: boolean }, ] - >({ + >(() => ({ queries: [ { queryKey: key1, @@ -511,7 +515,7 @@ describe('useQueries', () => { queryFn: () => ['string[]'], }, ], - }) + })) expectType>(result1[0]) expectType>(result1[1]) expectType>(result1[2]) @@ -526,7 +530,7 @@ describe('useQueries', () => { { queryFnData: string; data: string }, { queryFnData: string; data: number }, ] - >({ + >(() => ({ queries: [ { queryKey: key1, @@ -547,35 +551,37 @@ describe('useQueries', () => { }, }, ], - }) + })) expectType>(result2[0]) expectType>(result2[1]) expectType(result2[0].data) expectType(result2[1].data) // can pass only TData (data prop) although TQueryFnData will be left unknown - const result3 = createQueries<[{ data: string }, { data: number }]>({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectType(a) - expectTypeNotAny(a) - return a as string + const result3 = createQueries<[{ data: string }, { data: number }]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a as string + }, }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectType(a) - expectTypeNotAny(a) - return a as number + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a as number + }, }, - }, - ], - }) + ], + }), + ) expectType>(result3[0]) expectType>(result3[1]) expectType(result3[0].data) @@ -587,7 +593,7 @@ describe('useQueries', () => { { queryFnData: string; data: string }, { queryFnData: string; data: number; error: boolean }, ] - >({ + >(() => ({ queries: [ { queryKey: key1, @@ -626,19 +632,17 @@ describe('useQueries', () => { initialData: 123, }, ], - }) + })) // field names should be enforced - createQueries<[{ queryFnData: string }]>({ + createQueries<[{ queryFnData: string }]>(() => ({ queries: [ { queryKey: key1, queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], }, ], - }) + })) } }) @@ -652,26 +656,26 @@ describe('useQueries', () => { // eslint-disable-next-line function Page() { // Array.map preserves TQueryFnData - const result1 = createQueries({ + const result1 = createQueries(() => ({ queries: Array(50).map((_, i) => ({ - queryKey: () => ['key', i] as const, + queryKey: ['key', i] as const, queryFn: () => i + 10, })), - }) + })) expectType[]>(result1) expectType(result1[0]?.data) // Array.map preserves TData - const result2 = createQueries({ + const result2 = createQueries(() => ({ queries: Array(50).map((_, i) => ({ - queryKey: () => ['key', i] as const, + queryKey: ['key', i] as const, queryFn: () => i + 10, select: (data: number) => data.toString(), })), - }) + })) expectType[]>(result2) - const result3 = createQueries({ + const result3 = createQueries(() => ({ queries: [ { queryKey: key1, @@ -687,7 +691,7 @@ describe('useQueries', () => { select: () => 123, }, ], - }) + })) expectType>(result3[0]) expectType>(result3[1]) expectType>(result3[2]) @@ -697,7 +701,7 @@ describe('useQueries', () => { expectType(result3[2].data) // initialData/placeholderData are enforced - createQueries({ + createQueries(() => ({ queries: [ { queryKey: key1, @@ -714,10 +718,10 @@ describe('useQueries', () => { initialData: 123, }, ], - }) + })) // select / onSuccess / onSettled params are "indirectly" enforced - createQueries({ + createQueries(() => ({ queries: [ // unfortunately TS will not suggest the type for you { @@ -760,10 +764,10 @@ describe('useQueries', () => { }, }, ], - }) + })) // callbacks are also indirectly enforced with Array.map - createQueries({ + createQueries(() => ({ // @ts-expect-error (onSuccess only accepts string) queries: Array(50).map((_, i) => ({ queryKey: ['key', i] as const, @@ -771,18 +775,19 @@ describe('useQueries', () => { select: (data: number) => data.toString(), onSuccess: (_data: number) => null, })), - }) - createQueries({ + })) + + createQueries(() => ({ queries: Array(50).map((_, i) => ({ - queryKey: () => ['key', i] as const, + queryKey: ['key', i] as const, queryFn: () => i + 10, select: (data: number) => data.toString(), onSuccess: (_data: string) => null, })), - }) + })) // results inference works when all the handlers are defined - const result4 = createQueries({ + const result4 = createQueries(() => ({ queries: [ { queryKey: key1, @@ -815,13 +820,13 @@ describe('useQueries', () => { }, }, ], - }) + })) expectType>(result4[0]) expectType>(result4[1]) expectType>(result4[2]) // handles when queryFn returns a Promise - const result5 = createQueries({ + const result5 = createQueries(() => ({ queries: [ { queryKey: key1, @@ -834,68 +839,67 @@ describe('useQueries', () => { onSettled: (a: Promise) => null, }, ], - }) + })) expectType>(result5[0]) // Array as const does not throw error - const result6 = createQueries({ - queries: [ - { - queryKey: () => ['key1'], - queryFn: () => 'string', - }, - { - queryKey: () => ['key1'], - queryFn: () => 123, - }, - ], - } as const) + const result6 = createQueries( + () => + ({ + queries: [ + { + queryKey: ['key1'], + queryFn: () => 'string', + }, + { + queryKey: ['key1'], + queryFn: () => 123, + }, + ], + } as const), + ) expectType>(result6[0]) expectType>(result6[1]) // field names should be enforced - array literal - createQueries({ + createQueries(() => ({ queries: [ { queryKey: key1, queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], }, ], - }) + })) // field names should be enforced - Array.map() result - createQueries({ + createQueries(() => ({ // @ts-expect-error (invalidField) queries: Array(10).map(() => ({ someInvalidField: '', })), - }) + })) // field names should be enforced - array literal - createQueries({ + createQueries(() => ({ queries: [ { queryKey: key1, queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], }, ], - }) + })) // supports queryFn using fetch() to return Promise - Array.map() result - createQueries({ + createQueries(() => ({ queries: Array(50).map((_, i) => ({ - queryKey: () => ['key', i] as const, + queryKey: ['key', i] as const, queryFn: () => fetch('return Promise').then((resp) => resp.json()), })), - }) + })) // supports queryFn using fetch() to return Promise - array literal - createQueries({ + createQueries(() => ({ queries: [ { queryKey: key1, @@ -903,7 +907,7 @@ describe('useQueries', () => { fetch('return Promise').then((resp) => resp.json()), }, ], - }) + })) } }) @@ -932,63 +936,61 @@ describe('useQueries', () => { TQueryFnData, TError, TData, - TQueryKey extends SolidQueryKey, - >(queries: CreateQueryOptions[]) { - return createQueries({ + TQueryKey extends QueryKey, + >(queries: SolidQueryOptions[]) { + return createQueries(() => ({ queries: queries.map( // no need to type the mapped query (query) => { const { queryFn: fn, queryKey: key, onError: err } = query - expectType< - QueryFunction> | undefined - >(fn) + expectType | undefined>(fn) return { queryKey: key, onError: err, queryFn: fn - ? (ctx: QueryFunctionContext>) => { - expectType>(ctx.queryKey) + ? (ctx: QueryFunctionContext) => { + expectType(ctx.queryKey) return fn.call({}, ctx) } : undefined, } }, ), - }) + })) } // @ts-expect-error (Page component is not rendered) // eslint-disable-next-line function Page() { - const result = createQueries({ + const result = createQueries(() => ({ queries: [ { - queryKey: () => getQueryKeyA(), + queryKey: getQueryKeyA(), queryFn: getQueryFunctionA(), }, { - queryKey: () => getQueryKeyB('id'), + queryKey: getQueryKeyB('id'), queryFn: getQueryFunctionB(), }, ], - }) + })) expectType>(result[0]) expectType>(result[1]) - const withSelector = createQueries({ + const withSelector = createQueries(() => ({ queries: [ { - queryKey: () => getQueryKeyA(), + queryKey: getQueryKeyA(), queryFn: getQueryFunctionA(), select: getSelectorA(), }, { - queryKey: () => getQueryKeyB('id'), + queryKey: getQueryKeyB('id'), queryFn: getQueryFunctionB(), select: getSelectorB(), }, ], - }) + })) expectType>( withSelector[0], ) @@ -998,7 +1000,7 @@ describe('useQueries', () => { const withWrappedQueries = useWrappedQueries( Array(10).map(() => ({ - queryKey: () => getQueryKeyA(), + queryKey: getQueryKeyA(), queryFn: getQueryFunctionA(), select: getSelectorA(), })), @@ -1029,7 +1031,7 @@ describe('useQueries', () => { }) function Queries() { - createQueries({ + createQueries(() => ({ queries: [ { queryKey: key1, @@ -1039,7 +1041,7 @@ describe('useQueries', () => { }, }, ], - }) + })) return (
    @@ -1082,7 +1084,7 @@ describe('useQueries', () => { const results: CreateQueryResult[][] = [] function Page() { - const result = createQueries({ + const result = createQueries(() => ({ context, queries: [ { @@ -1100,7 +1102,7 @@ describe('useQueries', () => { }, }, ], - }) + })) createRenderEffect(() => { results.push([...result]) }) @@ -1133,7 +1135,7 @@ describe('useQueries', () => { const results: CreateQueryResult[][] = [] function Page() { - const result = createQueries({ + const result = createQueries(() => ({ queries: [ { queryKey: key1, @@ -1144,7 +1146,7 @@ describe('useQueries', () => { queryFn: async () => 2, }, ], - }) + })) results.push(result) return null } diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 7a093ce266..4e2b1e7c3c 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -41,73 +41,76 @@ describe('createQuery', () => { // eslint-disable-next-line function Page() { // unspecified query function should default to unknown - const noQueryFn = createQuery({ queryKey: key }) + const noQueryFn = createQuery(() => ({ queryKey: key })) expectType(noQueryFn.data) expectType(noQueryFn.error) // it should infer the result type from the query function - const fromQueryFn = createQuery({ queryKey: key, queryFn: () => 'test' }) + const fromQueryFn = createQuery(() => ({ + queryKey: key, + queryFn: () => 'test', + })) expectType(fromQueryFn.data) expectType(fromQueryFn.error) // it should be possible to specify the result type - const withResult = createQuery({ + const withResult = createQuery(() => ({ queryKey: key, queryFn: () => 'test', - }) + })) expectType(withResult.data) expectType(withResult.error) // it should be possible to specify the error type - const withError = createQuery({ + const withError = createQuery(() => ({ queryKey: key, queryFn: () => 'test', - }) + })) expectType(withError.data) expectType(withError.error) // it should provide the result type in the configuration - createQuery({ - queryKey: () => [key()], + createQuery(() => ({ + queryKey: [key], queryFn: async () => true, onSuccess: (data) => expectType(data), onSettled: (data) => expectType(data), - }) + })) // it should be possible to specify a union type as result type - const unionTypeSync = createQuery({ + const unionTypeSync = createQuery(() => ({ queryKey: key, queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'), onSuccess: (data) => expectType<'a' | 'b'>(data), - }) + })) expectType<'a' | 'b' | undefined>(unionTypeSync.data) - const unionTypeAsync = createQuery<'a' | 'b'>({ + const unionTypeAsync = createQuery<'a' | 'b'>(() => ({ queryKey: key, queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), onSuccess: (data) => expectType<'a' | 'b'>(data), - }) + })) expectType<'a' | 'b' | undefined>(unionTypeAsync.data) // should error when the query function result does not match with the specified type // @ts-expect-error - createQuery(key, () => 'test') + createQuery(() => ({ queryKey: key, queryFn: () => 'test' })) // it should infer the result type from a generic query function function queryFn(): Promise { return Promise.resolve({} as T) } - const fromGenericQueryFn = createQuery({ + const fromGenericQueryFn = createQuery(() => ({ queryKey: key, queryFn: () => queryFn(), - }) + })) expectType(fromGenericQueryFn.data) expectType(fromGenericQueryFn.error) - const fromGenericOptionsQueryFn = createQuery({ + const fromGenericOptionsQueryFn = createQuery(() => ({ queryKey: key, queryFn: () => queryFn(), - }) + })) expectType(fromGenericOptionsQueryFn.data) expectType(fromGenericOptionsQueryFn.error) @@ -120,39 +123,39 @@ describe('createQuery', () => { return n + 42 } - createQuery({ - queryKey: () => ['my-data', 100] as const, + createQuery(() => ({ + queryKey: ['my-data', 100] as const, queryFn: getMyDataArrayKey, - }) + })) - const getMyDataStringKey: QueryFunction = async ( + const getMyDataStringKey: QueryFunction = async ( context, ) => { - expectType(context.queryKey) + expectType<['1']>(context.queryKey) return Number(context.queryKey[0]) + 42 } - createQuery({ - queryKey: () => ['1'] as const, + createQuery(() => ({ + queryKey: ['1'] as ['1'], queryFn: getMyDataStringKey, - }) + })) // it should handle query-functions that return Promise - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: () => fetch('return Promise').then((resp) => resp.json()), - }) + })) // handles wrapped queries with custom fetcher passed as inline queryFn const useWrappedQuery = < - TQueryKey extends () => [string, Record?], + TQueryKey extends [string, Record?], TQueryFnData, TError, TData = TQueryFnData, >( qk: TQueryKey, fetcher: ( - obj: ReturnType[1], + obj: TQueryKey[1], token: string, // return type must be wrapped with TQueryFnReturn ) => Promise, @@ -161,20 +164,17 @@ describe('createQuery', () => { 'queryKey' | 'queryFn' | 'initialData' >, ) => - createQuery({ + createQuery(() => ({ queryKey: qk, - queryFn: () => fetcher(qk()[1], 'token'), + queryFn: () => fetcher(qk[1], 'token'), ...options, - }) - const test = useWrappedQuery( - () => [''], - async () => '1', - ) + })) + const test = useWrappedQuery([''], async () => '1') expectType(test.data) // handles wrapped queries with custom fetcher passed directly to createQuery const useWrappedFuncStyleQuery = < - TQueryKey extends () => [string, Record?], + TQueryKey extends [string, Record?], TQueryFnData, TError, TData = TQueryFnData, @@ -185,11 +185,8 @@ describe('createQuery', () => { CreateQueryOptions, 'queryKey' | 'queryFn' | 'initialData' >, - ) => createQuery({ queryKey: qk, queryFn: fetcher, ...options }) - const testFuncStyle = useWrappedFuncStyleQuery( - () => [''], - async () => true, - ) + ) => createQuery(() => ({ queryKey: qk, queryFn: fetcher, ...options })) + const testFuncStyle = useWrappedFuncStyleQuery([''], async () => true) expectType(testFuncStyle.data) } }) @@ -199,13 +196,13 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) return (
    @@ -230,13 +227,13 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page(): JSX.Element { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -336,12 +333,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject('rejected'), retry: 1, retryDelay: 1, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -450,14 +447,16 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - // TODO(lukemurray): do we want reactivity on this key? await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: () => 'prefetched', }) function Page() { - const state = createQuery({ queryKey: key, queryFn: () => 'data' }) + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => 'data', + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -491,14 +490,14 @@ describe('createQuery', () => { const onSuccess = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'data' }, onSuccess, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -517,67 +516,18 @@ describe('createQuery', () => { expect(onSuccess).toHaveBeenCalledWith('data') }) - it('should call onSuccess after a query has been refetched', async () => { - const key = queryKey() - const states: CreateQueryResult[] = [] - const onSuccess = jest.fn() - let count = 0 - - function Page() { - const state = createQuery({ - queryKey: key, - queryFn: async () => { - count++ - await sleep(10) - return 'data' + count - }, - onSuccess, - }) - - createRenderEffect( - on( - () => [state.data, state.refetch], - () => { - states.push(state) - }, - ), - ) - - return ( -
    -
    data: {state.data}
    - -
    - ) - } - - render(() => ( - - - - )) - - await screen.findByText('data: data1') - fireEvent.click(screen.getByRole('button', { name: /refetch/i })) - await screen.findByText('data: data2') - - expect(states.length).toBe(3) //loading, success, success after refetch - expect(count).toBe(2) - expect(onSuccess).toHaveBeenCalledTimes(2) - }) - it('should call onSuccess after a disabled query has been fetched', async () => { const key = queryKey() const states: CreateQueryResult[] = [] const onSuccess = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', enabled: false, onSuccess, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -619,14 +569,14 @@ describe('createQuery', () => { } function Component() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'data' }, onSuccess, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -650,12 +600,13 @@ describe('createQuery', () => { const onError = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject('error'), retry: false, onError, - }) + })) + createRenderEffect(() => { states.push({ ...state }) }) @@ -680,14 +631,14 @@ describe('createQuery', () => { const onError = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 23 }, onError, - }) + })) return ( status: {state.status}, fetchStatus: {state.fetchStatus} @@ -702,7 +653,7 @@ describe('createQuery', () => { )) await sleep(5) - await queryClient.cancelQueries({ queryKey: key() }) + await queryClient.cancelQueries({ queryKey: key }) // query cancellation will reset the query to it's initial state await waitFor(() => screen.getByText('status: loading, fetchStatus: idle')) expect(onError).not.toHaveBeenCalled() @@ -714,11 +665,12 @@ describe('createQuery', () => { const onSettled = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', onSettled, - }) + })) + createRenderEffect(() => { states.push({ ...state }) }) @@ -743,12 +695,12 @@ describe('createQuery', () => { const onSettled = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject('error'), retry: false, onSettled, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -772,7 +724,7 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { fetchCount++ @@ -781,7 +733,7 @@ describe('createQuery', () => { }, enabled: false, initialData: 'initialData', - }) + })) createEffect(() => { setActTimeout(() => { @@ -811,7 +763,7 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { fetchCount++ @@ -820,7 +772,7 @@ describe('createQuery', () => { }, enabled: false, initialData: 'initialData', - }) + })) createEffect(() => { setActTimeout(() => { @@ -850,7 +802,7 @@ describe('createQuery', () => { let fetchCount = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { fetchCount++ @@ -858,7 +810,7 @@ describe('createQuery', () => { return 'data' }, enabled: false, - }) + })) createEffect(() => { setActTimeout(() => { @@ -887,11 +839,10 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - // TODO(lukemurray): do we want this to be reactive. - queryClient.setQueryDefaults(key(), { queryFn: () => 'data' }) + queryClient.setQueryDefaults(key, { queryFn: () => 'data' }) function Page() { - const state = createQuery({ queryKey: key }) + const state = createQuery(() => ({ queryKey: key })) createRenderEffect(() => { states.push({ ...state }) }) @@ -934,15 +885,14 @@ describe('createQuery', () => { } function Component({ value }: { value: string }) { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'data: ' + value }, cacheTime: 0, - notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -997,11 +947,11 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'test', refetchOnMount: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -1025,14 +975,14 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - queryClient.setQueryData(key(), 'prefetched') + queryClient.setQueryData(key, 'prefetched') function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'test', refetchOnMount: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -1056,11 +1006,11 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => ({ name: 'test' }), select: (data) => data.name, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -1085,11 +1035,40 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => ({ name: 'test' }), select: (data) => data.name, + })) + createRenderEffect(() => { + states.push({ ...state }) }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should be able to select a part of the data with select in object syntax', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => ({ name: 'test' }), + select: (data) => data.name, + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -1114,12 +1093,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => ({ name: 'test' }), select: (data) => data.name, notifyOnChangeProps: ['data'], - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1154,13 +1133,13 @@ describe('createQuery', () => { const error = new Error('Select Error') function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => ({ name: 'test' }), select: () => { throw error }, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -1182,60 +1161,18 @@ describe('createQuery', () => { expect(states[1]).toMatchObject({ status: 'error', error }) }) - it.skip('should not re-run a stable select when it re-renders if selector throws an error', async () => { - const key = queryKey() - const error = new Error('Select Error') - let runs = 0 - - function Page() { - //@ts-expect-error -- we skip this test, and no such thing as rerender in solid - const [, rerender] = NotReact.useReducer(() => ({}), {}) - const state = createQuery({ - queryKey: key, - queryFn: () => (runs === 0 ? 'test' : 'test2'), - - select: () => { - runs++ - throw error - }, - }) - return ( -
    -
    error: {state.error?.message}
    - - -
    - ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('error: Select Error')) - expect(runs).toEqual(1) - fireEvent.click(screen.getByRole('button', { name: 'rerender' })) - await sleep(10) - expect(runs).toEqual(1) - fireEvent.click(screen.getByRole('button', { name: 'refetch' })) - await sleep(10) - expect(runs).toEqual(2) - }) - it('should track properties and only re-render when a tracked property changes', async () => { const key = queryKey() const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1277,7 +1214,10 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ queryKey: key, queryFn: () => 'test' }) + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => 'test', + })) createRenderEffect(() => { states.push({ ...state }) @@ -1329,7 +1269,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -1337,7 +1277,7 @@ describe('createQuery', () => { return count === 1 ? result1 : result2 }, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1387,10 +1327,10 @@ describe('createQuery', () => { const key = queryKey() const results: CreateQueryResult[] = [] - queryClient.setQueryData(key(), 'set') + queryClient.setQueryData(key, 'set') function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -1398,7 +1338,7 @@ describe('createQuery', () => { }, initialData: 'initial', staleTime: Infinity, - }) + })) createRenderEffect(() => { results.push({ ...result }) @@ -1407,9 +1347,7 @@ describe('createQuery', () => { return (
    isFetching: {result.isFetching}
    - data: {result.data} @@ -1440,7 +1378,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -1448,8 +1386,7 @@ describe('createQuery', () => { return count }, staleTime: Infinity, - notifyOnChangeProps: 'all', - }) + })) createEffect(() => { states.push({ ...state }) @@ -1458,7 +1395,7 @@ describe('createQuery', () => { return (
    @@ -1515,7 +1452,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -1523,7 +1460,7 @@ describe('createQuery', () => { return count }, enabled: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1531,7 +1468,7 @@ describe('createQuery', () => { createEffect(() => { setActTimeout(() => { - queryClient.refetchQueries({ queryKey: key() }) + queryClient.refetchQueries({ queryKey: key }) }, 20) }) @@ -1561,7 +1498,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -1569,7 +1506,7 @@ describe('createQuery', () => { return count }, enabled: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1577,7 +1514,7 @@ describe('createQuery', () => { createEffect(() => { setActTimeout(() => { - queryClient.invalidateQueries({ queryKey: key() }) + queryClient.invalidateQueries({ queryKey: key }) }, 20) }) @@ -1608,16 +1545,14 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: async () => { await sleep(5) return count() }, - get enabled() { - return count() === 0 - }, - }) + enabled: count() === 0, + })) createRenderEffect(() => { states.push({ ...state }) @@ -1669,14 +1604,14 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: async () => { await sleep(10) return count() }, keepPreviousData: true, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1729,118 +1664,6 @@ describe('createQuery', () => { }) }) - // this test relies on rerenders which don't exist in solid - it.skip('should transition to error state when keepPreviousData is set', async () => { - const key = queryKey() - const states: CreateQueryResult[] = [] - - function Page(props: { count: number }) { - const state = createQuery({ - queryKey: () => [key(), props.count], - queryFn: async () => { - await sleep(10) - if (props.count === 2) { - throw new Error('Error test') - } - return Promise.resolve(props.count) - }, - retry: false, - keepPreviousData: true, - }) - - createRenderEffect(() => { - states.push({ ...state }) - }) - - return ( -
    -

    data: {state.data}

    -

    error: {state.error?.message}

    -

    previous data: {state.isPreviousData}

    -
    - ) - } - - render(() => ( - - - - )) - await waitFor(() => screen.getByText('data: 0')) - // @ts-expect-error we skip this test and rerenders don't exist in solid - act(() => screen.rerender()) - await waitFor(() => screen.getByText('data: 1')) - // @ts-expect-error we skip this test and rerenders don't exist in solid - act(() => screen.rerender()) - await waitFor(() => screen.getByText('error: Error test')) - - await waitFor(() => expect(states.length).toBe(8)) - // Initial - expect(states[0]).toMatchObject({ - data: undefined, - isFetching: true, - status: 'loading', - error: null, - isPreviousData: false, - }) - // Fetched - expect(states[1]).toMatchObject({ - data: 0, - isFetching: false, - status: 'success', - error: null, - isPreviousData: false, - }) - // rerender Page 1 - expect(states[2]).toMatchObject({ - data: 0, - isFetching: true, - status: 'success', - error: null, - isPreviousData: true, - }) - // Hook state update - expect(states[3]).toMatchObject({ - data: 0, - isFetching: true, - status: 'success', - error: null, - isPreviousData: true, - }) - // New data - expect(states[4]).toMatchObject({ - data: 1, - isFetching: false, - status: 'success', - error: null, - isPreviousData: false, - }) - // rerender Page 2 - expect(states[5]).toMatchObject({ - data: 1, - isFetching: true, - status: 'success', - error: null, - isPreviousData: true, - }) - // Hook state update again - expect(states[6]).toMatchObject({ - data: 1, - isFetching: true, - status: 'success', - error: null, - isPreviousData: true, - }) - // Error - expect(states[7]).toMatchObject({ - data: undefined, - isFetching: false, - status: 'error', - isPreviousData: false, - }) - expect(states[7]?.error).toHaveProperty('message', 'Error test') - }) - it('should not show initial data from next query if keepPreviousData is set', async () => { const key = queryKey() const states: DefinedCreateQueryResult[] = [] @@ -1848,15 +1671,15 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: async () => { await sleep(10) return count() }, initialData: 99, keepPreviousData: true, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -1928,8 +1751,8 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: async () => { await sleep(10) return count() @@ -1937,7 +1760,7 @@ describe('createQuery', () => { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -2017,15 +1840,15 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - queryClient.setQueryData([key(), 10], 10) + queryClient.setQueryData([key, 10], 10) await sleep(10) function Page() { const [count, setCount] = createSignal(10) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: async () => { await sleep(10) return count() @@ -2033,7 +1856,7 @@ describe('createQuery', () => { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -2100,14 +1923,13 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function FirstComponent() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 1 }, - notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -2122,11 +1944,10 @@ describe('createQuery', () => { } function SecondComponent() { - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: () => 2, - notifyOnChangeProps: 'all', - }) + })) return null } @@ -2171,7 +1992,7 @@ describe('createQuery', () => { const states2: CreateQueryResult[] = [] await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: async () => { await sleep(10) return 'prefetch' @@ -2181,14 +2002,14 @@ describe('createQuery', () => { await sleep(20) function FirstComponent() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'one' }, staleTime: 100, - }) + })) createRenderEffect(() => { states1.push({ ...state }) }) @@ -2196,14 +2017,14 @@ describe('createQuery', () => { } function SecondComponent() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'two' }, staleTime: 10, - }) + })) createRenderEffect(() => { states2.push({ ...state }) }) @@ -2277,11 +2098,11 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'test', staleTime: 50, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2307,15 +2128,14 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(5) return 'test' }, - notifyOnChangeProps: ['data'], - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -2357,19 +2177,19 @@ describe('createQuery', () => { const key2 = queryKey() function Page() { - const first = createQuery({ + const first = createQuery(() => ({ queryKey: key1, queryFn: () => 'data', enabled: false, initialData: 'init', - }) + })) - const second = createQuery({ + const second = createQuery(() => ({ queryKey: key2, queryFn: () => 'data', enabled: false, initialData: 'init', - }) + })) return (
    @@ -2407,8 +2227,8 @@ describe('createQuery', () => { } function Page() { - createQuery({ queryKey: key, queryFn: queryFn1 }) - createQuery({ queryKey: key, queryFn: queryFn2 }) + createQuery(() => ({ queryKey: key, queryFn: queryFn1 })) + createQuery(() => ({ queryKey: key, queryFn: queryFn2 })) return null } @@ -2418,7 +2238,7 @@ describe('createQuery', () => { )) - expect(queryCache.find({ queryKey: key() })!.options.queryFn).toBe(queryFn1) + expect(queryCache.find({ queryKey: key })!.options.queryFn).toBe(queryFn1) }) it('should batch re-renders', async () => { @@ -2432,8 +2252,8 @@ describe('createQuery', () => { } function Page() { - createQuery({ queryKey: key, queryFn }) - createQuery({ queryKey: key, queryFn }) + createQuery(() => ({ queryKey: key, queryFn })) + createQuery(() => ({ queryKey: key, queryFn })) renders++ return null } @@ -2464,20 +2284,20 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - createQuery({ + createQuery(() => ({ queryKey: key, queryFn, onSuccess: () => { setCount((x) => x + 1) }, - }) - createQuery({ + })) + createQuery(() => ({ queryKey: key, queryFn, onSuccess: () => { setCount((x) => x + 1) }, - }) + })) createEffect(() => { renders++ @@ -2506,10 +2326,13 @@ describe('createQuery', () => { function Page() { const [, setNewState] = createSignal('state') - const state = createQuery({ queryKey: key, queryFn: () => 'data' }) + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => 'data', + })) createEffect(() => { setActTimeout(() => { - queryClient.setQueryData(key(), 'new') + queryClient.setQueryData(key, 'new') // Update with same state to make react discard the next render setNewState('state') }, 10) @@ -2532,12 +2355,15 @@ describe('createQuery', () => { const key2 = queryKey() function Page() { - const first = createQuery({ + const first = createQuery(() => ({ queryKey: key1, queryFn: () => 'data', enabled: false, - }) - const second = createQuery({ queryKey: key2, queryFn: () => 'data' }) + })) + const second = createQuery(() => ({ + queryKey: key2, + queryFn: () => 'data', + })) return (
    @@ -2569,13 +2395,13 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const { status } = createQuery({ + const { status } = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) return
    status: {status}
    } @@ -2593,19 +2419,18 @@ describe('createQuery', () => { it('should not pass stringified variables to query function', async () => { const key = queryKey() const variables = { number: 5, boolean: false, object: {}, array: [] } - type CustomQueryKey = readonly [ReturnType, typeof variables] - const states: CreateQueryResult[] = [] - - // TODO(lukemurray): extract the query function to a variable queryFn + type CustomQueryKey = readonly [typeof key, typeof variables] + const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ - queryKey: () => [key(), variables] as const, + const state = createQuery(() => ({ + queryKey: [key, variables] as const, queryFn: async (ctx) => { await sleep(10) return ctx.queryKey }, - }) + })) + createRenderEffect(() => { states.push({ ...state }) }) @@ -2620,7 +2445,7 @@ describe('createQuery', () => { await sleep(20) - expect(states[1]?.data).toEqual([key(), variables]) + expect(states[1]?.data).toEqual([key, variables]) }) it('should not refetch query on focus when `enabled` is set to `false`', async () => { @@ -2628,11 +2453,11 @@ describe('createQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Page() { - const { data = 'default' } = createQuery({ + const { data = 'default' } = createQuery(() => ({ queryKey: key, queryFn, enabled: false, - }) + })) return (
    @@ -2660,12 +2485,12 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2695,12 +2520,12 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => count++, staleTime: 0, refetchOnWindowFocus: () => false, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2730,12 +2555,12 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => count++, staleTime: Infinity, refetchOnWindowFocus: true, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2765,7 +2590,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -2773,7 +2598,7 @@ describe('createQuery', () => { }, staleTime: Infinity, refetchOnWindowFocus: 'always', - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2805,7 +2630,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -2814,7 +2639,7 @@ describe('createQuery', () => { staleTime: 0, retry: 0, refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2854,17 +2679,17 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: () => 'prefetched', }) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', refetchOnMount: 'always', staleTime: Infinity, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2897,19 +2722,19 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: () => 'prefetched', }) await sleep(10) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', refetchOnMount: true, staleTime: 0, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -2941,13 +2766,13 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => { return Promise.reject('Error test jaylen') }, retry: false, - }) + })) return (
    @@ -2971,12 +2796,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject('Error test jaylen'), retry: false, throwErrors: true, - }) + })) return (
    @@ -3003,11 +2828,11 @@ describe('createQuery', () => { let result: CreateQueryResult | undefined function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: () => Promise.resolve('data'), throwErrors: true, - }) + })) createEffect(() => { result = query @@ -3031,12 +2856,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject('Local Error'), retry: false, throwErrors: (err) => err !== 'Local Error', - }) + })) return (
    @@ -3062,12 +2887,12 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => Promise.reject(new Error('Remote Error')), retry: false, throwErrors: (err) => err.message !== 'Local Error', - }) + })) return (
    @@ -3102,7 +2927,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -3112,7 +2937,7 @@ describe('createQuery', () => { retry: 2, retryDelay: 100, - }) + })) return (
    @@ -3157,7 +2982,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -3166,7 +2991,7 @@ describe('createQuery', () => { }, retry: 2, retryDelay: 100, - }) + })) return (
    @@ -3185,9 +3010,7 @@ describe('createQuery', () => { return (
    - {show() && } @@ -3218,17 +3041,17 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: () => 'prefetched', }) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', refetchOnMount: 'always', staleTime: 50, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3272,11 +3095,11 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', initialData: 'initial', - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3310,12 +3133,12 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', staleTime: 50, initialData: 'initial', - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3350,13 +3173,13 @@ describe('createQuery', () => { const oneSecondAgo = Date.now() - 1000 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', staleTime: 50, initialData: 'initial', initialDataUpdatedAt: oneSecondAgo, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3394,13 +3217,13 @@ describe('createQuery', () => { const states: DefinedCreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', staleTime: 10 * 1000, // 10 seconds initialData: 'initial', initialDataUpdatedAt: 0, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3434,12 +3257,12 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ - queryKey: () => [key(), count()], + const state = createQuery(() => ({ + queryKey: [key, count()], queryFn: () => ({ count: 10 }), staleTime: Infinity, initialData: () => ({ count: count() }), - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3477,12 +3300,12 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn, retry: 1, retryDelay: 1, - }) + })) return (
    @@ -3523,12 +3346,12 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn, retryDelay: 1, retry: (_failureCount, err) => err !== 'NoRetry', - }) + })) return (
    @@ -3566,12 +3389,12 @@ describe('createQuery', () => { }) function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn, retry: 1, retryDelay: (_, error: DelayError) => error.delay, - }) + })) return (
    @@ -3608,7 +3431,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: () => { count++ @@ -3616,7 +3439,7 @@ describe('createQuery', () => { }, retry: 3, retryDelay: 1, - }) + })) return (
    @@ -3667,10 +3490,13 @@ describe('createQuery', () => { const key = queryKey() const states: CreateQueryResult[] = [] - queryClient.setQueryData(key(), 'prefetched') + queryClient.setQueryData(key, 'prefetched') function Page() { - const state = createQuery({ queryKey: key, queryFn: () => 'data' }) + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => 'data', + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3708,16 +3534,16 @@ describe('createQuery', () => { const visibilityMock = mockVisibilityState('hidden') // set data in cache to check if the hook query fn is actually called - queryClient.setQueryData(key(), 'prefetched') + queryClient.setQueryData(key, 'prefetched') function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'data' }, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3778,7 +3604,7 @@ describe('createQuery', () => { prefetchQueryFn.mockImplementation(() => 'not yet...') await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: prefetchQueryFn, staleTime: 10, }) @@ -3786,7 +3612,7 @@ describe('createQuery', () => { await sleep(11) function Page() { - const state = createQuery({ queryKey: key, queryFn }) + const state = createQuery(() => ({ queryKey: key, queryFn })) createRenderEffect(() => { states.push({ ...state }) }) @@ -3818,7 +3644,7 @@ describe('createQuery', () => { }) await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: prefetchQueryFn, staleTime: 1000, }) @@ -3826,7 +3652,7 @@ describe('createQuery', () => { await sleep(0) function Page() { - createQuery({ queryKey: key, queryFn, staleTime: 1000 }) + createQuery(() => ({ queryKey: key, queryFn, staleTime: 1000 })) return null } @@ -3849,7 +3675,7 @@ describe('createQuery', () => { function Page() { let counter = 0 - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: async () => { if (counter < 2) { @@ -3860,7 +3686,7 @@ describe('createQuery', () => { } }, retryDelay: 10, - }) + })) return (
    @@ -3891,22 +3717,20 @@ describe('createQuery', () => { const [enabled, setEnabled] = createSignal(false) const [isPrefetched, setPrefetched] = createSignal(false) - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ await sleep(10) return count }, - get enabled() { - return enabled() - }, - }) + enabled: enabled(), + })) createEffect(() => { async function prefetch() { await queryClient.prefetchQuery({ - queryKey: key(), + queryKey: key, queryFn: () => Promise.resolve('prefetched data'), }) setPrefetched(true) @@ -3942,13 +3766,11 @@ describe('createQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = createSignal(false) - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: () => 'data', - get enabled() { - return shouldFetch() - }, - }) + enabled: shouldFetch(), + })) return (
    @@ -3984,11 +3806,11 @@ describe('createQuery', () => { const results: DefinedCreateQueryResult[] = [] function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: () => 'serverData', initialData: 'data', - }) + })) createRenderEffect(() => { results.push({ ...result }) @@ -4015,11 +3837,11 @@ describe('createQuery', () => { const results: DefinedCreateQueryResult[] = [] function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: () => 1, initialData: 0, - }) + })) createRenderEffect(() => { results.push({ ...result }) @@ -4049,16 +3871,12 @@ describe('createQuery', () => { function Page() { const [shouldFetch, setShouldFetch] = createSignal(true) - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: () => 'fetched data', - get enabled() { - return shouldFetch() - }, - get initialData() { - return shouldFetch() ? 'initial' : 'initial falsy' - }, - }) + enabled: shouldFetch(), + initialData: shouldFetch() ? 'initial' : 'initial falsy', + })) createRenderEffect(() => { results.push({ ...result }) @@ -4092,11 +3910,12 @@ describe('createQuery', () => { queryFn.mockImplementation(() => 'data') function Page() { - const { fetchStatus } = createQuery({ + const { fetchStatus } = createQuery(() => ({ queryKey: key, queryFn, enabled: false, - }) + })) + return
    fetchStatus: {fetchStatus}
    } @@ -4107,7 +3926,7 @@ describe('createQuery', () => { )) expect(queryFn).not.toHaveBeenCalled() - expect(queryCache.find({ queryKey: key() })).not.toBeUndefined() + expect(queryCache.find({ queryKey: key })).not.toBeUndefined() screen.getByText('fetchStatus: idle') }) @@ -4116,11 +3935,11 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: () => 'data', enabled: false, - }) + })) return (
    @@ -4144,11 +3963,11 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const query = createQuery({ + const query = createQuery(() => ({ queryKey: key, queryFn: () => 'fetched data', cacheTime: Infinity, - }) + })) return
    {query.data}
    } @@ -4162,7 +3981,7 @@ describe('createQuery', () => { result.unmount() - const query = queryCache.find({ queryKey: key() }) + const query = queryCache.find({ queryKey: key }) // @ts-expect-error expect(query!.cacheTimeout).toBe(undefined) }) @@ -4173,7 +3992,7 @@ describe('createQuery', () => { const memoFn = jest.fn() function Page() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -4185,7 +4004,7 @@ describe('createQuery', () => { } ) }, - }) + })) createMemo(() => { memoFn() @@ -4222,13 +4041,11 @@ describe('createQuery', () => { function Page() { const [int, setInt] = createSignal(200) - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => count++, - get refetchInterval() { - return int() - }, - }) + refetchInterval: int(), + })) createEffect(() => { if (state.data === 2) { @@ -4257,14 +4074,14 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return count++ }, refetchInterval: (data = 0) => (data < 2 ? 10 : false), - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4329,11 +4146,11 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 1, refetchInterval: 0, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4370,10 +4187,10 @@ describe('createQuery', () => { it('should accept an empty string as query key', async () => { function Page() { - const result = createQuery({ - queryKey: () => [''], + const result = createQuery(() => ({ + queryKey: [''], queryFn: (ctx) => ctx.queryKey, - }) + })) return <>{JSON.stringify(result.data)} } @@ -4388,10 +4205,10 @@ describe('createQuery', () => { it('should accept an object as query key', async () => { function Page() { - const result = createQuery({ - queryKey: () => [{ a: 'a' }], + const result = createQuery(() => ({ + queryKey: [{ a: 'a' }], queryFn: (ctx) => ctx.queryKey, - }) + })) return <>{JSON.stringify(result.data)} } @@ -4410,19 +4227,17 @@ describe('createQuery', () => { const queryFn = jest.fn().mockReturnValue('data') function Disabled() { - createQuery({ queryKey: key, queryFn, enabled: false }) + createQuery(() => ({ queryKey: key, queryFn, enabled: false })) return null } function Page() { const [enabled, setEnabled] = createSignal(false) - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn, - get enabled() { - return enabled() - }, - }) + enabled: enabled(), + })) return ( <> @@ -4449,11 +4264,11 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: () => 'data', placeholderData: 'placeholder', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4496,14 +4311,12 @@ describe('createQuery', () => { function Page() { const [count, setCount] = createSignal(0) - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: () => 'data', placeholderData: 'placeholder', - get enabled() { - return count() === 0 - }, - }) + enabled: count() === 0, + })) createRenderEffect(() => { states.push({ state: { ...state }, count: count() }) @@ -4562,12 +4375,12 @@ describe('createQuery', () => { const states: CreateQueryResult[] = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: () => 1, placeholderData: 23, select: (data) => String(data * 2), - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4609,7 +4422,7 @@ describe('createQuery', () => { let placeholderFunctionRunCount = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: () => 1, placeholderData: () => { @@ -4617,7 +4430,7 @@ describe('createQuery', () => { return 23 }, select: (data) => String(data * 2), - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4654,58 +4467,6 @@ describe('createQuery', () => { expect(placeholderFunctionRunCount).toEqual(1) }) - // React Specific implementation. Not really needed since solid functions are stable - it.skip('select should only run when dependencies change if memoized', async () => { - const key1 = queryKey() - - let selectRun = 0 - - function Page() { - //@ts-expect-error skip this test - const [count, inc] = NotReact.useReducer((prev) => prev + 1, 2) - - const state = createQuery({ - queryKey: key1, - queryFn: async () => { - await sleep(10) - return 0 - }, - //@ts-expect-error skip this test - select: NotReact.useCallback( - (data: number) => { - selectRun++ - return `selected ${data + count}` - }, - [count], - ), - placeholderData: 99, - }) - - return ( -
    -

    Data: {state.data}

    - -
    - ) - } - - render(() => ( - - - - )) - await waitFor(() => screen.getByText('Data: selected 101')) // 99 + 2 - expect(selectRun).toBe(1) - - await waitFor(() => screen.getByText('Data: selected 2')) // 0 + 2 - expect(selectRun).toBe(2) - - fireEvent.click(screen.getByRole('button', { name: /inc/i })) - - await waitFor(() => screen.getByText('Data: selected 3')) // 0 + 3 - expect(selectRun).toBe(3) - }) - it('select should always return the correct state', async () => { const key1 = queryKey() @@ -4721,19 +4482,18 @@ describe('createQuery', () => { setForceValue((prev) => prev + 1) } - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: async () => { await sleep(10) return 0 }, - get select() { const currentCount = count() return (data: number) => `selected ${data + currentCount}` }, placeholderData: 99, - }) + })) return (
    @@ -4772,14 +4532,14 @@ describe('createQuery', () => { function Page() { const [forceValue, setForceValue] = createSignal(1) - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key1, queryFn: async () => { await sleep(10) return [1, 2] }, select: (res) => res.map((x) => x + 1), - }) + })) createEffect(() => { if (state.data) { @@ -4832,7 +4592,7 @@ describe('createQuery', () => { } function Page() { - const state = createQuery({ queryKey: key, queryFn }) + const state = createQuery(() => ({ queryKey: key, queryFn })) return (

    Status: {state.status}

    @@ -4859,7 +4619,7 @@ describe('createQuery', () => { const queryFn: QueryFunction< string, - readonly [ReturnType, number] + readonly [typeof key, number] > = async (ctx) => { const [, limit] = ctx.queryKey const value = limit % 2 && ctx.signal ? 'abort' : `data ${limit}` @@ -4868,10 +4628,10 @@ describe('createQuery', () => { } function Page(props: { limit: number }) { - const state = createQuery({ - queryKey: () => [key(), props.limit] as const, + const state = createQuery(() => ({ + queryKey: [key, props.limit] as const, queryFn, - }) + })) states[props.limit] = state return (
    @@ -4897,25 +4657,25 @@ describe('createQuery', () => { await waitFor(() => expect(states).toHaveLength(4)) - expect(queryCache.find({ queryKey: [key(), 0] })?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 0] })?.state).toMatchObject({ data: 'data 0', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find({ queryKey: [key(), 1] })?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 1] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', }) - expect(queryCache.find({ queryKey: [key(), 2] })?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 2] })?.state).toMatchObject({ data: 'data 2', status: 'success', dataUpdateCount: 1, }) - expect(queryCache.find({ queryKey: [key(), 3] })?.state).toMatchObject({ + expect(queryCache.find({ queryKey: [key, 3] })?.state).toMatchObject({ data: undefined, status: 'loading', fetchStatus: 'idle', @@ -4935,7 +4695,7 @@ describe('createQuery', () => { const [id, setId] = createSignal(1) const [hasChanged, setHasChanged] = createSignal(false) - const state = createQuery({ queryKey: () => [key(), id()], queryFn }) + const state = createQuery(() => ({ queryKey: [key, id()], queryFn })) createRenderEffect(() => { states.push({ ...state }) @@ -4978,7 +4738,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -4986,7 +4746,7 @@ describe('createQuery', () => { return count }, staleTime: Infinity, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -4994,7 +4754,7 @@ describe('createQuery', () => { return (
    -
    data: {state.data ?? 'null'}
    @@ -5054,7 +4814,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -5064,7 +4824,7 @@ describe('createQuery', () => { staleTime: Infinity, enabled: false, notifyOnChangeProps: 'all', - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -5075,7 +4835,7 @@ describe('createQuery', () => { return (
    -
    data: {state.data ?? 'null'}
    @@ -5142,11 +4902,12 @@ describe('createQuery', () => { } function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'test', queryKeyHashFn, - }) + })) + createEffect( on( () => state.status, @@ -5177,17 +4938,15 @@ describe('createQuery', () => { }) function Page(props: { enabled: boolean }) { - const state = createQuery({ - queryKey: () => ['key'], + const state = createQuery(() => ({ + queryKey: ['key'], queryFn, - get enabled() { - return props.enabled - }, + enabled: props.enabled, retry: false, retryOnMount: false, refetchOnMount: false, refetchOnWindowFocus: false, - }) + })) return ( rendered
    }> @@ -5240,8 +4999,8 @@ describe('createQuery', () => { it('should refetch when query key changed when previous status is error', async () => { function Page(props: { id: number }) { - const state = createQuery({ - queryKey: () => [props.id], + const state = createQuery(() => ({ + queryKey: [props.id], queryFn: async () => { await sleep(10) if (props.id % 2 === 1) { @@ -5254,7 +5013,7 @@ describe('createQuery', () => { retryOnMount: false, refetchOnMount: false, refetchOnWindowFocus: false, - }) + })) return ( rendered
    }> @@ -5305,8 +5064,8 @@ describe('createQuery', () => { it('should refetch when query key changed when switching between erroneous queries', async () => { function Page(props: { id: boolean }) { - const state = createQuery({ - queryKey: () => [props.id], + const state = createQuery(() => ({ + queryKey: [props.id], queryFn: async () => { await sleep(10) return Promise.reject(new Error('Error')) @@ -5315,7 +5074,7 @@ describe('createQuery', () => { retryOnMount: false, refetchOnMount: false, refetchOnWindowFocus: false, - }) + })) return ( rendered
    }> @@ -5373,7 +5132,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -5384,7 +5143,7 @@ describe('createQuery', () => { return 5 }, retry: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -5451,13 +5210,13 @@ describe('createQuery', () => { const states: Array = [] function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'data' }, - }) + })) createEffect(() => { states.push(state.fetchStatus) @@ -5499,14 +5258,14 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ await sleep(10) return 'data' + count }, - }) + })) return (
    @@ -5517,7 +5276,7 @@ describe('createQuery', () => {
    failureReason: {state.failureReason ?? 'null'}
    data: {state.data}
    @@ -5569,14 +5328,14 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ await sleep(10) return 'data' + count }, - }) + })) return (
    @@ -5585,7 +5344,7 @@ describe('createQuery', () => {
    data: {state.data}
    @@ -5623,14 +5382,14 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ await sleep(10) return 'data' + count }, - }) + })) return (
    @@ -5639,7 +5398,7 @@ describe('createQuery', () => {
    data: {state.data}
    @@ -5677,7 +5436,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -5685,7 +5444,7 @@ describe('createQuery', () => { return 'data' + count }, initialData: 'initial', - }) + })) return (
    @@ -5694,7 +5453,7 @@ describe('createQuery', () => {
    data: {state.data}
    @@ -5735,7 +5494,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -5743,7 +5502,7 @@ describe('createQuery', () => { return 'data' + count }, initialData: 'initial', - }) + })) return (
    @@ -5752,7 +5511,7 @@ describe('createQuery', () => {
    data: {state.data}
    @@ -5806,7 +5565,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async (): Promise => { count++ @@ -5815,7 +5574,7 @@ describe('createQuery', () => { }, retry: 2, retryDelay: 10, - }) + })) return (
    @@ -5872,14 +5631,14 @@ describe('createQuery', () => { let count = 0 function Component() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ await sleep(10) return 'data' + count }, - }) + })) return (
    @@ -5921,7 +5680,7 @@ describe('createQuery', () => { await sleep(15) - expect(queryClient.getQueryState(key())).toMatchObject({ + expect(queryClient.getQueryState(key)).toMatchObject({ fetchStatus: 'idle', status: 'success', }) @@ -5936,7 +5695,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -5944,12 +5703,12 @@ describe('createQuery', () => { return 'data' + count }, refetchOnReconnect: false, - }) + })) return (
    @@ -6000,14 +5759,14 @@ describe('createQuery', () => { let count = 0 function Component() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async ({ signal }) => { count++ await sleep(10) return `${signal ? 'signal' : 'data'}${count}` }, - }) + })) return (
    @@ -6027,7 +5786,7 @@ describe('createQuery', () => { {show() && } @@ -6062,7 +5821,7 @@ describe('createQuery', () => { await sleep(15) - expect(queryClient.getQueryState(key())).toMatchObject({ + expect(queryClient.getQueryState(key)).toMatchObject({ fetchStatus: 'idle', status: 'success', }) @@ -6081,7 +5840,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { count++ @@ -6089,7 +5848,7 @@ describe('createQuery', () => { return 'data ' + count }, networkMode: 'always', - }) + })) return (
    @@ -6123,7 +5882,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async (): Promise => { count++ @@ -6133,7 +5892,7 @@ describe('createQuery', () => { networkMode: 'always', retry: 1, retryDelay: 5, - }) + })) return (
    @@ -6173,7 +5932,7 @@ describe('createQuery', () => { let count = 0 function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async (): Promise => { count++ @@ -6183,7 +5942,7 @@ describe('createQuery', () => { retry: 2, retryDelay: 1, networkMode: 'offlineFirst', - }) + })) return (
    @@ -6235,12 +5994,12 @@ describe('createQuery', () => { } function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn, retry: false, retryOnMount: false, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -6249,7 +6008,7 @@ describe('createQuery', () => { return <> } - await queryClient.prefetchQuery({ queryKey: key(), queryFn }) + await queryClient.prefetchQuery({ queryKey: key, queryFn }) render(() => ( @@ -6269,15 +6028,15 @@ describe('createQuery', () => { const onSuccess = jest.fn() function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: () => 'data', onSuccess, - }) + })) return (
    data: {state.data}
    -
    @@ -6302,14 +6061,17 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery({ queryKey: key, queryFn: () => 'data' }) + const state = createQuery(() => ({ + queryKey: key, + queryFn: () => 'data', + })) return (
    data: {state.data}
    dataUpdatedAt: {state.dataUpdatedAt}
    diff --git a/packages/solid-query/src/__tests__/createQuery.types.test.tsx b/packages/solid-query/src/__tests__/createQuery.types.test.tsx index eda398c8bc..a21a610eca 100644 --- a/packages/solid-query/src/__tests__/createQuery.types.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.types.test.tsx @@ -1,4 +1,4 @@ -import { createQuery } from '../createQuery' +import { createQuery } from '../index' export type Equal = (() => T extends X ? 1 : 2) extends < T, @@ -14,8 +14,8 @@ describe('initialData', () => { describe('Config object overload', () => { it('TData should always be defined when initialData is provided as an object', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -24,7 +24,7 @@ describe('initialData', () => { initialData: { wow: true, }, - }) + })) const result: Expect> = true return result @@ -33,8 +33,8 @@ describe('initialData', () => { it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -43,7 +43,7 @@ describe('initialData', () => { initialData: () => ({ wow: true, }), - }) + })) const result: Expect> = true return result @@ -52,14 +52,14 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, } }, - }) + })) const result: Expect> = true @@ -69,15 +69,15 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, } }, initialData: () => undefined as { wow: boolean } | undefined, - }) + })) const result: Expect> = true @@ -89,8 +89,8 @@ describe('initialData', () => { describe('Query key overload', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -99,7 +99,7 @@ describe('initialData', () => { initialData: { wow: true, }, - }) + })) const result: Expect> = true return result @@ -108,14 +108,14 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, } }, - }) + })) const result: Expect> = true @@ -127,8 +127,8 @@ describe('initialData', () => { describe('Query key and func', () => { it('TData should always be defined when initialData is provided', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, @@ -138,7 +138,7 @@ describe('initialData', () => { initialData: { wow: true, }, - }) + })) const result: Expect> = true return result @@ -147,14 +147,14 @@ describe('initialData', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { doNotExecute(() => { - const { data } = createQuery({ - queryKey: () => ['key'], + const { data } = createQuery(() => ({ + queryKey: ['key'], queryFn: () => { return { wow: true, } }, - }) + })) const result: Expect> = true diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index 80a57250c2..d9b3d588a6 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -29,24 +29,23 @@ describe("useQuery's in Suspense mode", () => { let renders = 0 function Page() { - const [stateKey, setStateKey] = createSignal(key()) + const [stateKey, setStateKey] = createSignal(key) - const state = createQuery({ - queryKey: stateKey, + const state = createQuery(() => ({ + queryKey: stateKey(), queryFn: async () => { count++ await sleep(10) return count }, - suspense: true, - }) + })) createRenderEffect(() => { states.push({ ...state }) }) createRenderEffect( - on([() => ({ ...state }), key], () => { + on([() => ({ ...state }), () => key], () => { renders++ }), ) @@ -73,9 +72,6 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('data: 2')) expect(renders).toBe(4) - // TODO(lukemurray): verify that this expectation is valid. this is 2 in - // react, but 4 in solid, because in solid suspense is triggered on read and - // the component needs to render in order to trigger suspense. expect(states.length).toBe(4) expect(states[1]).toMatchObject({ data: 1, status: 'success' }) expect(states[3]).toMatchObject({ data: 2, status: 'success' }) @@ -87,8 +83,8 @@ describe("useQuery's in Suspense mode", () => { function Page() { const [multiplier, setMultiplier] = createSignal(1) - const state = createInfiniteQuery({ - queryKey: () => [`${key()}_${multiplier()}`], + const state = createInfiniteQuery(() => ({ + queryKey: [`${key}_${multiplier()}`], queryFn: async ({ pageParam = 1 }) => { await sleep(10) return Number(pageParam * multiplier()) @@ -96,7 +92,7 @@ describe("useQuery's in Suspense mode", () => { suspense: true, getNextPageParam: (lastPage) => lastPage + 1, - }) + })) createRenderEffect(() => { states.push({ ...state }) @@ -149,7 +145,7 @@ describe("useQuery's in Suspense mode", () => { }) function Page() { - createQuery({ queryKey: () => [key()], queryFn, suspense: true }) + createQuery(() => ({ queryKey: [key], queryFn, suspense: true })) return <>rendered } @@ -171,14 +167,13 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: () => { sleep(10) return 'data' }, - suspense: true, - }) + })) return <>rendered } @@ -204,17 +199,17 @@ describe("useQuery's in Suspense mode", () => { )) expect(screen.queryByText('rendered')).toBeNull() - expect(queryCache.find({ queryKey: key() })).toBeFalsy() + expect(queryCache.find({ queryKey: key })).toBeFalsy() fireEvent.click(screen.getByLabelText('toggle')) await waitFor(() => screen.getByText('rendered')) - expect(queryCache.find({ queryKey: key() })?.getObserversCount()).toBe(1) + expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(1) fireEvent.click(screen.getByLabelText('toggle')) expect(screen.queryByText('rendered')).toBeNull() - expect(queryCache.find({ queryKey: key() })?.getObserversCount()).toBe(0) + expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(0) }) it('should call onSuccess on the first successful call', async () => { @@ -223,17 +218,17 @@ describe("useQuery's in Suspense mode", () => { const successFn = jest.fn() function Page() { - createQuery({ - queryKey: () => [key()], + createQuery(() => ({ + queryKey: [key], queryFn: async () => { await sleep(10) - return key() + return key }, suspense: true, select: () => 'selected', onSuccess: successFn, - }) + })) return <>rendered } @@ -259,31 +254,27 @@ describe("useQuery's in Suspense mode", () => { const successFn2 = jest.fn() function FirstComponent() { - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: () => { sleep(10) return 'data' }, - - suspense: true, onSuccess: successFn1, - }) + })) return first } function SecondComponent() { - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: () => { sleep(10) return 'data' }, - - suspense: true, onSuccess: successFn2, - }) + })) return second } @@ -311,21 +302,19 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) - if (!succeed) { throw new Error('Suspense Error Bingo') } else { return 'data' } }, - retryDelay: 10, suspense: true, - }) + })) // Suspense only triggers if used in JSX return ( @@ -376,7 +365,7 @@ describe("useQuery's in Suspense mode", () => { let succeed = false function Page() { - const state = createQuery({ + const state = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) @@ -386,10 +375,9 @@ describe("useQuery's in Suspense mode", () => { return 'data' } }, - retry: false, suspense: true, - }) + })) // Suspense only triggers if used in JSX return ( @@ -438,7 +426,7 @@ describe("useQuery's in Suspense mode", () => { let count = 0 function Component() { - const result = createQuery({ + const result = createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(100) @@ -449,7 +437,7 @@ describe("useQuery's in Suspense mode", () => { retry: false, suspense: true, staleTime: 0, - }) + })) return (
    data: {result.data} @@ -497,26 +485,25 @@ describe("useQuery's in Suspense mode", () => { const key2 = queryKey() function Component(props: { queryKey: Array }) { - const result = createQuery({ - queryKey: () => props.queryKey, + const result = createQuery(() => ({ + queryKey: props.queryKey, queryFn: async () => { await sleep(100) return props.queryKey }, - retry: false, suspense: true, - }) + })) return
    data: {result.data}
    } function Page() { - const [key, setKey] = createSignal(key1()) + const [key, setKey] = createSignal(key1) return (
    } @@ -81,24 +78,24 @@ describe('useIsFetching', () => { } function FirstQuery() { - createQuery({ + createQuery(() => ({ queryKey: key1, queryFn: async () => { - await sleep(100) + await sleep(150) return 'data' }, - }) + })) return null } function SecondQuery() { - createQuery({ + createQuery(() => ({ queryKey: key2, queryFn: async () => { - await sleep(100) + await sleep(200) return 'data' }, - }) + })) return null } @@ -108,7 +105,7 @@ describe('useIsFetching', () => { createEffect(() => { setActTimeout(() => { setRenderSecond(true) - }, 50) + }, 100) }) return ( @@ -139,30 +136,34 @@ describe('useIsFetching', () => { const isFetchings: number[] = [] function One() { - createQuery({ + createQuery(() => ({ queryKey: key1, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) return null } function Two() { - createQuery({ + createQuery(() => ({ queryKey: key2, queryFn: async () => { await sleep(20) return 'test' }, - }) + })) return null } function Page() { const [started, setStarted] = createSignal(false) - const isFetching = useIsFetching({ queryKey: key1 }) + const isFetching = useIsFetching(() => ({ + filters: { + queryKey: key1, + }, + })) createRenderEffect(() => { isFetchings.push(isFetching()) @@ -207,20 +208,21 @@ describe('useIsFetching', () => { function Page() { const [ready, setReady] = createSignal(false) - const isFetching = useIsFetching(undefined, { context: context }) + const isFetching = useIsFetching(() => ({ + options: { + context, + }, + })) - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(50) return 'test' }, - - get enabled() { - return ready() - }, + enabled: ready(), context, - }) + })) return (
    @@ -252,13 +254,13 @@ describe('useIsFetching', () => { function Page() { const isFetching = useIsFetching() - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: async () => 'test', enabled: true, context, throwErrors: true, - }) + })) return (
    @@ -284,13 +286,13 @@ describe('useIsFetching', () => { const key = queryKey() function Page() { - createQuery({ + createQuery(() => ({ queryKey: key, queryFn: async () => { await sleep(10) return 'test' }, - }) + })) const isFetching = useIsFetching() diff --git a/packages/solid-query/src/__tests__/useIsMutating.test.tsx b/packages/solid-query/src/__tests__/useIsMutating.test.tsx index eff08bc7d2..642392774e 100644 --- a/packages/solid-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/solid-query/src/__tests__/useIsMutating.test.tsx @@ -33,20 +33,20 @@ describe('useIsMutating', () => { } function Mutations() { - const { mutate: mutate1 } = createMutation({ + const { mutate: mutate1 } = createMutation(() => ({ mutationKey: ['mutation1'], mutationFn: async () => { await sleep(150) return 'data' }, - }) - const { mutate: mutate2 } = createMutation({ + })) + const { mutate: mutate2 } = createMutation(() => ({ mutationKey: ['mutation2'], mutationFn: async () => { await sleep(50) return 'data' }, - }) + })) createEffect(() => { mutate1() @@ -80,7 +80,9 @@ describe('useIsMutating', () => { const queryClient = createQueryClient() function IsMutating() { - const isMutating = useIsMutating({ mutationKey: ['mutation1'] }) + const isMutating = useIsMutating(() => ({ + filters: { mutationKey: ['mutation1'] }, + })) createRenderEffect(() => { isMutatings.push(isMutating()) }) @@ -88,20 +90,20 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation({ + const { mutate: mutate1 } = createMutation(() => ({ mutationKey: ['mutation1'], mutationFn: async () => { await sleep(100) return 'data' }, - }) - const { mutate: mutate2 } = createMutation({ + })) + const { mutate: mutate2 } = createMutation(() => ({ mutationKey: ['mutation2'], mutationFn: async () => { await sleep(100) return 'data' }, - }) + })) createEffect(() => { mutate1() @@ -125,10 +127,12 @@ describe('useIsMutating', () => { const queryClient = createQueryClient() function IsMutating() { - const isMutating = useIsMutating({ - predicate: (mutation) => - mutation.options.mutationKey?.[0] === 'mutation1', - }) + const isMutating = useIsMutating(() => ({ + filters: { + predicate: (mutation) => + mutation.options.mutationKey?.[0] === 'mutation1', + }, + })) createRenderEffect(() => { isMutatings.push(isMutating()) }) @@ -136,20 +140,20 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation({ + const { mutate: mutate1 } = createMutation(() => ({ mutationKey: ['mutation1'], mutationFn: async () => { await sleep(100) return 'data' }, - }) - const { mutate: mutate2 } = createMutation({ + })) + const { mutate: mutate2 } = createMutation(() => ({ mutationKey: ['mutation2'], mutationFn: async () => { await sleep(100) return 'data' }, - }) + })) createEffect(() => { mutate1() @@ -194,13 +198,13 @@ describe('useIsMutating', () => { function Page() { const [mounted, setMounted] = createSignal(true) - const { mutate: mutate1 } = createMutation({ + const { mutate: mutate1 } = createMutation(() => ({ mutationKey: ['mutation1'], mutationFn: async () => { await sleep(10) return 'data' }, - }) + })) createEffect(() => { mutate1() @@ -238,7 +242,11 @@ describe('useIsMutating', () => { const queryClient = new QueryClient() function IsMutating() { - const isMutating = useIsMutating(undefined, { context }) + const isMutating = useIsMutating(() => ({ + options: { + context, + }, + })) createRenderEffect(() => { isMutatings.push(isMutating()) @@ -248,22 +256,22 @@ describe('useIsMutating', () => { } function Page() { - const { mutate: mutate1 } = createMutation({ + const { mutate: mutate1 } = createMutation(() => ({ mutationKey: ['mutation1'], mutationFn: async () => { await sleep(150) return 'data' }, context, - }) - const { mutate: mutate2 } = createMutation({ + })) + const { mutate: mutate2 } = createMutation(() => ({ mutationKey: ['mutation2'], mutationFn: async () => { await sleep(50) return 'data' }, context, - }) + })) createEffect(() => { mutate1() @@ -296,12 +304,12 @@ describe('useIsMutating', () => { } function Page() { - const { mutate } = createMutation({ + const { mutate } = createMutation(() => ({ mutationKey: ['mutation'], mutationFn: async () => 'data', throwErrors: true, context, - }) + })) createEffect(() => { mutate() diff --git a/packages/solid-query/src/__tests__/utils.tsx b/packages/solid-query/src/__tests__/utils.tsx index 41c904f127..964d696593 100644 --- a/packages/solid-query/src/__tests__/utils.tsx +++ b/packages/solid-query/src/__tests__/utils.tsx @@ -4,9 +4,9 @@ import type { ParentProps } from 'solid-js' import { createEffect, createSignal, onCleanup, Show } from 'solid-js' let queryKeyCount = 0 -export function queryKey(): () => Array { - const localQueryKeyCount = queryKeyCount++ - return () => [`query_${localQueryKeyCount}`] +export function queryKey(): Array { + queryKeyCount++ + return [`query_${queryKeyCount}`] } export const Blink = ( diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts index 7df614a9f0..32c2b03c21 100644 --- a/packages/solid-query/src/createBaseQuery.ts +++ b/packages/solid-query/src/createBaseQuery.ts @@ -1,16 +1,22 @@ -import type { QueryObserver } from '@tanstack/query-core' -import type { QueryKey, QueryObserverResult } from '@tanstack/query-core' -import type { CreateBaseQueryOptions } from './types' -import { useQueryClient } from './QueryClientProvider' +import type { + QueryKey, + QueryObserver, + QueryObserverResult, +} from '@tanstack/query-core' +import { notifyManager } from '@tanstack/query-core' +import type { Accessor } from 'solid-js' import { - onMount, - onCleanup, + batch, createComputed, + createMemo, createResource, on, - batch, + onCleanup, + onMount, } from 'solid-js' import { createStore, unwrap } from 'solid-js/store' +import { useQueryClient } from './QueryClientProvider' +import type { CreateBaseQueryOptions } from './types' import { shouldThrowError } from './utils' // Base Query Function that is used to create the query. @@ -21,23 +27,21 @@ export function createBaseQuery< TQueryData, TQueryKey extends QueryKey, >( - options: CreateBaseQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey + options: Accessor< + CreateBaseQueryOptions >, Observer: typeof QueryObserver, -): QueryObserverResult { - const queryClient = useQueryClient({ context: options.context }) +) { + const queryClient = createMemo(() => + useQueryClient({ context: options().context }), + ) const emptyData = Symbol('empty') - const defaultedOptions = queryClient.defaultQueryOptions(options) + + const defaultedOptions = queryClient().defaultQueryOptions(options()) defaultedOptions._optimisticResults = 'optimistic' - const observer = new Observer(queryClient, defaultedOptions) + const observer = new Observer(queryClient(), defaultedOptions) const [state, setState] = createStore>( - // @ts-ignore observer.getOptimisticResult(defaultedOptions), ) @@ -59,32 +63,19 @@ export function createBaseQuery< refetch() }) - let taskQueue: Array<() => void> = [] - const unsubscribe = observer.subscribe((result) => { - taskQueue.push(() => { - batch(() => { - const unwrappedResult = { ...unwrap(result) } - if (unwrappedResult.data === undefined) { - // This is a hack to prevent Solid - // from deleting the data property when it is `undefined` - // ref: https://www.solidjs.com/docs/latest/api#updating-stores - // @ts-ignore - unwrappedResult.data = emptyData - } - setState(unwrap(unwrappedResult)) - mutate(() => unwrap(result.data)) - refetch() - }) - }) - - queueMicrotask(() => { - const taskToRun = taskQueue.pop() - if (taskToRun) { - taskToRun() + notifyManager.batchCalls(() => { + const unwrappedResult = { ...unwrap(result) } + if (unwrappedResult.data === undefined) { + // This is a hack to prevent Solid + // from deleting the data property when it is `undefined` + // ref: https://www.solidjs.com/docs/latest/api#updating-stores + unwrappedResult.data = emptyData as any as undefined } - taskQueue = [] - }) + setState(unwrap(unwrappedResult)) + mutate(() => unwrap(result.data)) + refetch() + })() }) onCleanup(() => unsubscribe()) @@ -94,8 +85,7 @@ export function createBaseQuery< }) createComputed(() => { - const newDefaultedOptions = queryClient.defaultQueryOptions(options) - observer.setOptions(newDefaultedOptions) + observer.setOptions(queryClient().defaultQueryOptions(options())) }) createComputed( @@ -128,5 +118,5 @@ export function createBaseQuery< }, } - return new Proxy(state, handler) as QueryObserverResult + return new Proxy(state, handler) } diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts index 78eb47c73c..7250865ab2 100644 --- a/packages/solid-query/src/createInfiniteQuery.ts +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -1,43 +1,22 @@ -import type { QueryObserver, QueryOptions } from '@tanstack/query-core' +import type { QueryObserver, QueryKey } from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, CreateInfiniteQueryResult, - SolidQueryKey, } from './types' import { createBaseQuery } from './createBaseQuery' -import { createComputed } from 'solid-js' -import { createStore } from 'solid-js/store' -import { normalizeQueryOptions } from './utils' +import { createMemo } from 'solid-js' export function createInfiniteQuery< TQueryFnData, TError, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, + TQueryKey extends QueryKey = QueryKey, >( - options: CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - >, + options: CreateInfiniteQueryOptions, ): CreateInfiniteQueryResult { - // The parseQuery Args functions helps normalize the arguments into the correct form. - // Whatever the parameters are, they are normalized into the correct form. - const [parsedOptions, setParsedOptions] = createStore( - normalizeQueryOptions(options), - ) - - // Watch for changes in the options and update the parsed options. - createComputed(() => { - const newParsedOptions = normalizeQueryOptions(options) - setParsedOptions(newParsedOptions) - }) - return createBaseQuery( - parsedOptions as QueryOptions>, + createMemo(() => options()), InfiniteQueryObserver as typeof QueryObserver, ) as CreateInfiniteQueryResult } diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index 6cb2b6376d..868ec62bb1 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -16,14 +16,13 @@ export function createMutation< TVariables = void, TContext = unknown, >( - mutationOptions: CreateMutationOptions, + options: CreateMutationOptions, ): CreateMutationResult { - const [options, setOptions] = createStore(mutationOptions) - const queryClient = useQueryClient({ context: options.context }) + const queryClient = useQueryClient({ context: options().context }) const observer = new MutationObserver( queryClient, - options, + options(), ) const mutate: CreateMutateFunction = ( @@ -42,8 +41,7 @@ export function createMutation< }) createComputed(() => { - setOptions(mutationOptions) - observer.setOptions(mutationOptions) + observer.setOptions(options()) }) createComputed( diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index 1fc449641d..f5258799ea 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -1,14 +1,9 @@ +import type { QueryFunction, QueryKey } from '@tanstack/query-core' +import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { createComputed, onCleanup, onMount } from 'solid-js' -import type { QueryFunction } from '@tanstack/query-core' -import { QueriesObserver } from '@tanstack/query-core' -import { useQueryClient } from './QueryClientProvider' -import type { - CreateQueryOptions, - CreateQueryResult, - SolidQueryKey, -} from './types' import { createStore, unwrap } from 'solid-js/store' -import { scheduleMicrotask } from './utils' +import { useQueryClient } from './QueryClientProvider' +import type { CreateQueryResult, SolidQueryOptions } from './types' // This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. // - `context` is omitted as it is passed as a root-level option to `useQueries` instead. @@ -16,8 +11,8 @@ type CreateQueryOptionsForCreateQueries< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, -> = Omit, 'context'> + TQueryKey extends QueryKey = QueryKey, +> = Omit, 'context'> // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 @@ -50,14 +45,14 @@ type GetOptions = TQueryFnData, unknown, TData, - () => TQueryKey + TQueryKey > : T extends { queryFn?: QueryFunction } ? CreateQueryOptionsForCreateQueries< TQueryFnData, unknown, TQueryFnData, - () => TQueryKey + TQueryKey > : // Fallback CreateQueryOptionsForCreateQueries @@ -143,26 +138,19 @@ export type QueriesResults< : // Fallback CreateQueryResult[] -type ArrType = T extends (infer U)[] ? U : never +export function createQueries( + queriesOptions: () => { + queries: readonly [...QueriesOptions] + context?: SolidQueryOptions['context'] + }, +): QueriesResults { + const queryClient = useQueryClient({ context: queriesOptions().context }) -export function createQueries(queriesOptions: { - queries: readonly [...QueriesOptions] - context?: CreateQueryOptions['context'] -}): QueriesResults { - const queryClient = useQueryClient({ context: queriesOptions.context }) - - const normalizeOptions = ( - options: ArrType, - ) => { - const normalizedOptions = { ...options, queryKey: options.queryKey?.() } - const defaultedOptions = queryClient.defaultQueryOptions(normalizedOptions) + const defaultedQueries = queriesOptions().queries.map((options) => { + const defaultedOptions = queryClient.defaultQueryOptions(options) defaultedOptions._optimisticResults = 'optimistic' return defaultedOptions - } - - const defaultedQueries = queriesOptions.queries.map((options) => - normalizeOptions(options), - ) + }) const observer = new QueriesObserver(queryClient, defaultedQueries) @@ -170,20 +158,10 @@ export function createQueries(queriesOptions: { observer.getOptimisticResult(defaultedQueries), ) - const taskQueue: Array<() => void> = [] - const unsubscribe = observer.subscribe((result) => { - taskQueue.push(() => { + notifyManager.batchCalls(() => { setState(unwrap(result)) - }) - - scheduleMicrotask(() => { - const taskToRun = taskQueue.pop() - if (taskToRun) { - taskToRun() - taskQueue.splice(0, taskQueue.length) - } - }) + })() }) onCleanup(unsubscribe) @@ -193,10 +171,12 @@ export function createQueries(queriesOptions: { }) createComputed(() => { - const updateDefaultedQueries = queriesOptions.queries.map((options) => - normalizeOptions(options), - ) - observer.setQueries(updateDefaultedQueries) + const updatedQueries = queriesOptions().queries.map((options) => { + const defaultedOptions = queryClient.defaultQueryOptions(options) + defaultedOptions._optimisticResults = 'optimistic' + return defaultedOptions + }) + observer.setQueries(updatedQueries) }) return state as QueriesResults diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts index 1b48a77d66..c264b7a2b2 100644 --- a/packages/solid-query/src/createQuery.ts +++ b/packages/solid-query/src/createQuery.ts @@ -1,42 +1,42 @@ -import type { QueryOptions } from '@tanstack/query-core' +import type { QueryKey } from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' +import { createMemo } from 'solid-js' +import { createBaseQuery } from './createBaseQuery' import type { CreateQueryOptions, CreateQueryResult, DefinedCreateQueryResult, - SolidQueryKey, + FunctionedParams, + SolidQueryOptions, } from './types' -import { createComputed } from 'solid-js' -import { createStore } from 'solid-js/store' -import { normalizeQueryOptions } from './utils' -import { createBaseQuery } from './createBaseQuery' type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, -> = CreateQueryOptions & { - initialData?: undefined -} + TQueryKey extends QueryKey = QueryKey, +> = FunctionedParams< + SolidQueryOptions & { + initialData?: undefined + } +> type DefinedInitialDataOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, -> = CreateQueryOptions & { - initialData: TQueryFnData | (() => TQueryFnData) -} - -// There is one way to create a query. -// 1. createQuery(options: CreateQueryOptions) + TQueryKey extends QueryKey = QueryKey, +> = FunctionedParams< + SolidQueryOptions & { + initialData: TQueryFnData | (() => TQueryFnData) + } +> export function createQuery< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, + TQueryKey extends QueryKey = QueryKey, >( options: UndefinedInitialDataOptions, ): CreateQueryResult @@ -45,7 +45,7 @@ export function createQuery< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, + TQueryKey extends QueryKey = QueryKey, >( options: DefinedInitialDataOptions, ): DefinedCreateQueryResult @@ -54,21 +54,10 @@ export function createQuery< TQueryFnData, TError, TData = TQueryFnData, - TQueryKey extends SolidQueryKey = SolidQueryKey, ->( - options: CreateQueryOptions, -): CreateQueryResult { - const [parsedOptions, setParsedOptions] = createStore( - normalizeQueryOptions(options), - ) - // Watch for changes in the options and update the parsed options. - createComputed(() => { - const newParsedOptions = normalizeQueryOptions(options) - setParsedOptions(newParsedOptions) - }) - + TQueryKey extends QueryKey = QueryKey, +>(options: CreateQueryOptions) { return createBaseQuery( - parsedOptions as QueryOptions>, + createMemo(() => options()), QueryObserver, ) } diff --git a/packages/solid-query/src/index.tsx b/packages/solid-query/src/index.ts similarity index 92% rename from packages/solid-query/src/index.tsx rename to packages/solid-query/src/index.ts index 7090337d2f..56ba952485 100644 --- a/packages/solid-query/src/index.tsx +++ b/packages/solid-query/src/index.ts @@ -1,5 +1,8 @@ /* istanbul ignore file */ +// Side Effects +import './setBatchUpdatesFn' + // Re-export core export * from '@tanstack/query-core' @@ -13,7 +16,7 @@ export { } from './QueryClientProvider' export type { QueryClientProviderProps } from './QueryClientProvider' export { useIsFetching } from './useIsFetching' -export { useIsMutating } from './useIsMutating' -export { createMutation } from './createMutation' export { createInfiniteQuery } from './createInfiniteQuery' +export { createMutation } from './createMutation' +export { useIsMutating } from './useIsMutating' export { createQueries } from './createQueries' diff --git a/packages/solid-query/src/setBatchUpdatesFn.ts b/packages/solid-query/src/setBatchUpdatesFn.ts new file mode 100644 index 0000000000..f53c357200 --- /dev/null +++ b/packages/solid-query/src/setBatchUpdatesFn.ts @@ -0,0 +1,4 @@ +import { notifyManager } from '@tanstack/query-core' +import { batch } from 'solid-js' + +notifyManager.setBatchNotifyFunction(batch) diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index 2b3e56f0a7..f3fffa3c71 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -12,7 +12,7 @@ import type { DefinedQueryObserverResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, - QueryFilters, + WithRequired, } from '@tanstack/query-core' export interface ContextOptions { @@ -22,8 +22,7 @@ export interface ContextOptions { context?: Context } -/* --- Create Query and Create Base Query Types --- */ -export type SolidQueryKey = () => readonly unknown[] +export type FunctionedParams = () => T export interface CreateBaseQueryOptions< TQueryFnData = unknown, @@ -32,25 +31,35 @@ export interface CreateBaseQueryOptions< TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends ContextOptions, - QueryObserverOptions {} + WithRequired< + QueryObserverOptions, + 'queryKey' + > {} -export interface CreateQueryOptions< +export interface SolidQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, - TQueryKey extends () => readonly unknown[] = SolidQueryKey, -> extends Omit< + TQueryKey extends QueryKey = QueryKey, +> extends WithRequired< CreateBaseQueryOptions< TQueryFnData, TError, TData, TQueryFnData, - ReturnType + TQueryKey >, 'queryKey' - > { - queryKey: TQueryKey -} + > {} + +export type CreateQueryOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = FunctionedParams> + +/* --- Create Query and Create Base Query Types --- */ export type CreateBaseQueryResult< TData = unknown, @@ -73,12 +82,12 @@ export type DefinedCreateQueryResult< > = DefinedCreateBaseQueryResult /* --- Create Infinite Queries Types --- */ -export interface CreateInfiniteQueryOptions< +export interface SolidInfiniteQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryData = TQueryFnData, - TQueryKey extends () => readonly unknown[] = SolidQueryKey, + TQueryKey extends QueryKey = QueryKey, > extends ContextOptions, Omit< InfiniteQueryObserverOptions< @@ -86,20 +95,35 @@ export interface CreateInfiniteQueryOptions< TError, TData, TQueryData, - ReturnType + TQueryKey >, 'queryKey' > { queryKey: TQueryKey } +export type CreateInfiniteQueryOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = FunctionedParams< + SolidInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + > +> + export type CreateInfiniteQueryResult< TData = unknown, TError = unknown, > = InfiniteQueryObserverResult /* --- Create Mutation Types --- */ -export interface CreateMutationOptions< +export interface SolidMutationOptions< TData = unknown, TError = unknown, TVariables = void, @@ -110,6 +134,13 @@ export interface CreateMutationOptions< '_defaulted' | 'variables' > {} +export type CreateMutationOptions< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +> = FunctionedParams> + export type CreateMutateFunction< TData = unknown, TError = unknown, @@ -146,11 +177,3 @@ export type CreateMutationResult< > = CreateBaseMutationResult type Override = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } - -/* --- Use Is Fetching Types --- */ -export interface SolidQueryFilters extends Omit { - queryKey?: SolidQueryKey -} - -export type ParseFilterArgs = - T['queryKey'] extends () => infer R ? T & { queryKey: R } : T diff --git a/packages/solid-query/src/useIsFetching.ts b/packages/solid-query/src/useIsFetching.ts index 3ff57bb89d..3e069496d2 100644 --- a/packages/solid-query/src/useIsFetching.ts +++ b/packages/solid-query/src/useIsFetching.ts @@ -1,50 +1,29 @@ import type { QueryFilters } from '@tanstack/query-core' - -import type { ContextOptions, SolidQueryFilters } from './types' -import { useQueryClient } from './QueryClientProvider' import type { Accessor } from 'solid-js' -import { createSignal, onCleanup, createComputed, createMemo } from 'solid-js' -import { normalizeFilterArgs } from './utils' - -interface Options extends ContextOptions {} - -export function useIsFetching( - filtersArgs?: SolidQueryFilters, - optionsArgs?: Options, -): Accessor { - const [filtersObj, optionsObj = {}] = normalizeFilterArgs( - filtersArgs, - optionsArgs, - ) +import { createMemo, createSignal, onCleanup } from 'solid-js' +import { useQueryClient } from './QueryClientProvider' +import type { ContextOptions } from './types' - const [filters, setFilters] = createSignal(filtersObj) - const [options, setOptions] = createSignal(optionsObj) +type Options = () => { + filters?: QueryFilters + options?: ContextOptions +} +export function useIsFetching(options: Options = () => ({})): Accessor { const queryClient = createMemo(() => - useQueryClient({ context: options().context }), + useQueryClient({ context: options().options?.context }), ) const queryCache = createMemo(() => queryClient().getQueryCache()) const [fetches, setFetches] = createSignal( - queryClient().isFetching(filters as QueryFilters), + queryClient().isFetching(options().filters), ) - createComputed(() => { - const [newFiltersObj, newOptionsObj = {}] = normalizeFilterArgs( - filtersArgs, - optionsArgs, - ) - setFilters(newFiltersObj) - setOptions(newOptionsObj) - }) - const unsubscribe = queryCache().subscribe(() => { - setFetches(queryClient().isFetching(filters() as QueryFilters)) + setFetches(queryClient().isFetching(options().filters)) }) - onCleanup(() => { - unsubscribe() - }) + onCleanup(unsubscribe) return fetches } diff --git a/packages/solid-query/src/useIsMutating.ts b/packages/solid-query/src/useIsMutating.ts index d4a2e586bd..5ef921b79f 100644 --- a/packages/solid-query/src/useIsMutating.ts +++ b/packages/solid-query/src/useIsMutating.ts @@ -2,28 +2,28 @@ import type { MutationFilters } from '@tanstack/query-core' import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' import type { Accessor } from 'solid-js' -import { createSignal, onCleanup } from 'solid-js' +import { createSignal, onCleanup, createMemo } from 'solid-js' -interface Options extends ContextOptions {} +type Options = () => { + filters?: MutationFilters + options?: ContextOptions +} -export function useIsMutating( - filters?: MutationFilters, - options: Options = {}, -): Accessor { - const queryClient = useQueryClient({ context: options.context }) - const mutationCache = queryClient.getMutationCache() +export function useIsMutating(options: Options = () => ({})): Accessor { + const queryClient = createMemo(() => + useQueryClient({ context: options().options?.context }), + ) + const mutationCache = createMemo(() => queryClient().getMutationCache()) const [mutations, setMutations] = createSignal( - queryClient.isMutating(filters), + queryClient().isMutating(options().filters), ) - const unsubscribe = mutationCache.subscribe((_result) => { - setMutations(queryClient.isMutating(filters)) + const unsubscribe = mutationCache().subscribe((_result) => { + setMutations(queryClient().isMutating(options().filters)) }) - onCleanup(() => { - unsubscribe() - }) + onCleanup(unsubscribe) return mutations } diff --git a/packages/solid-query/src/utils.ts b/packages/solid-query/src/utils.ts index 4a04558d76..212f4a4817 100644 --- a/packages/solid-query/src/utils.ts +++ b/packages/solid-query/src/utils.ts @@ -1,35 +1,3 @@ -import type { SolidQueryKey, SolidQueryFilters, ParseFilterArgs } from './types' - -export function isQueryKey(value: unknown): value is SolidQueryKey { - return typeof value === 'function' -} - -// The parseQuery Args functions helps normalize the arguments into the correct form. -// Whatever the parameters are, they are normalized into the correct form. -export function normalizeQueryOptions(arg: T): T { - const { queryKey: solidKey, ...opts } = arg as any - if (solidKey) { - return { - ...opts, - queryKey: solidKey(), - } - } - return arg as any -} - -export function normalizeFilterArgs< - TFilters extends SolidQueryFilters, - TOptions = unknown, ->( - arg1?: TFilters, - arg2?: TOptions, -): [ParseFilterArgs, TOptions | undefined] { - return [{ ...arg1, queryKey: arg1?.queryKey?.() }, arg2] as [ - ParseFilterArgs, - TOptions, - ] -} - export function shouldThrowError boolean>( throwError: boolean | T | undefined, params: Parameters, @@ -41,17 +9,3 @@ export function shouldThrowError boolean>( return !!throwError } - -export function sleep(timeout: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, timeout) - }) -} - -/** - * Schedules a microtask. - * This can be useful to schedule state updates after rendering. - */ -export function scheduleMicrotask(callback: () => void) { - sleep(0).then(callback) -} diff --git a/rollup.config.ts b/rollup.config.ts index af9309c622..ee5ffe47fc 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -120,12 +120,12 @@ export default function rollup(options: RollupOptions): RollupOptions[] { '@tanstack/react-query': 'ReactQuery', '@tanstack/match-sorter-utils': 'MatchSorterUtils', 'use-sync-external-store/shim/index.js': 'UseSyncExternalStore', - "superjson": 'SuperJson', + superjson: 'SuperJson', }, bundleUMDGlobals: [ '@tanstack/match-sorter-utils', 'use-sync-external-store/shim/index.js', - "superjson", + 'superjson', ], }), ...buildConfigs({ @@ -140,7 +140,7 @@ export default function rollup(options: RollupOptions): RollupOptions[] { '@tanstack/react-query': 'ReactQuery', '@tanstack/match-sorter-utils': 'MatchSorterUtils', 'use-sync-external-store/shim/index.js': 'UseSyncExternalStore', - "superjson": 'SuperJson', + superjson: 'SuperJson', }, forceDevEnv: true, forceBundle: true, @@ -164,7 +164,7 @@ export default function rollup(options: RollupOptions): RollupOptions[] { packageDir: 'packages/solid-query', jsName: 'SolidQuery', outputFile: 'index', - entryFile: 'src/index.tsx', + entryFile: 'src/index.ts', globals: { 'solid-js/store': 'SolidStore', 'solid-js': 'Solid', From 22be6d5e20f0411d9697d87df150f81cf22e8776 Mon Sep 17 00:00:00 2001 From: Judicael <46365844+judicaelandria@users.noreply.github.com> Date: Wed, 4 Jan 2023 22:31:48 +0300 Subject: [PATCH 007/314] feat(isDataEqual): Remove is data equal from useQuery (#4720) * feat(isDataEqual): Remove is data equal from useQuery * Remove references and add migration guide * change diff code v5 migration Co-authored-by: Dominik Dorfmeister --- docs/react/guides/important-defaults.md | 2 +- .../guides/migrating-to-react-query-5.md | 17 ++++++++++- docs/react/reference/useQuery.md | 7 +---- packages/query-core/src/queryObserver.ts | 11 ------- .../query-core/src/tests/queryClient.test.tsx | 14 --------- .../src/tests/queryObserver.test.tsx | 29 ------------------- packages/query-core/src/types.ts | 1 - packages/query-core/src/utils.ts | 5 +--- 8 files changed, 19 insertions(+), 67 deletions(-) diff --git a/docs/react/guides/important-defaults.md b/docs/react/guides/important-defaults.md index 6d12ba6f5b..f4416a599c 100644 --- a/docs/react/guides/important-defaults.md +++ b/docs/react/guides/important-defaults.md @@ -30,7 +30,7 @@ If you see a refetch that you are not expecting, it is likely because you just f - Query results by default are **structurally shared to detect if data has actually changed** and if not, **the data reference remains unchanged** to better help with value stabilization with regards to useMemo and useCallback. If this concept sounds foreign, then don't worry about it! 99.9% of the time you will not need to disable this and it makes your app more performant at zero cost to you. - > Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can define a data compare function with `config.isDataEqual` or provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required. + > Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required. [//]: # 'Materials' diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index 89e637710f..c068c1cd9a 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -117,6 +117,21 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. + +### The `isDataEqual` options has been removed from useQuery + +Previously, This function was used to indicate whether to use previous `data` (`true`) or new data (`false`) as a resolved data for the query. + +You can achieve the same functionality by passing a function to `structuralSharing` instead: + +```diff + import { replaceEqualDeep } from '@tanstack/react-query' + +- isDataEqual: (oldData, newData) => customCheck(oldData, newData) ++ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) +``` + ### The `useErrorBoundary` prop has been renamed to `throwErrors` -To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. \ No newline at end of file +To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. + diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index eecd1e23a1..9358686904 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -168,12 +168,7 @@ const { - Optional - Defaults to `false` - If set, any previous `data` will be kept when fetching new data because the query key changed. -- `isDataEqual: (oldData: TData | undefined, newData: TData) => boolean` - - **Deprecated**. You can achieve the same functionality by passing a function to `structuralSharing` instead: - - structuralSharing: (oldData, newData) => isDataEqual(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) - - Optional - - This function should return boolean indicating whether to use previous `data` (`true`) or new data (`false`) as a resolved data for the query. -- `structuralSharing: boolean | ((oldData: TData | undefined, newData: TData) => TData)` + `structuralSharing: boolean | ((oldData: TData | undefined, newData: TData) => TData)` - Optional - Defaults to `true` - If set to `false`, structural sharing between query results will be disabled. diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 22832d023f..96ccd0a378 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -154,17 +154,6 @@ export class QueryObserver< this.options = this.client.defaultQueryOptions(options) - if ( - process.env.NODE_ENV !== 'production' && - typeof options?.isDataEqual !== 'undefined' - ) { - this.client - .getLogger() - .error( - `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`, - ) - } - if (!shallowEqualObjects(prevOptions, this.options)) { this.client.getQueryCache().notify({ type: 'observerOptionsUpdated', diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 051ea85c21..bb6881b12a 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -338,20 +338,6 @@ describe('queryClient', () => { ) }) - test('should use prev data if an isDataEqual function is defined and returns "true"', () => { - const key = queryKey() - - queryClient.setDefaultOptions({ - queries: { isDataEqual: (_prev, data) => data === 'data' }, - }) - queryClient.setQueryData(key, 'prev data') - queryClient.setQueryData(key, 'data') - - expect(queryCache.find({ queryKey: key })!.state.data).toEqual( - 'prev data', - ) - }) - test('should set the new data without comparison if structuralSharing is set to false', () => { const key = queryKey() diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index a87e76cf0b..5e30ff0431 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -649,35 +649,6 @@ describe('queryObserver', () => { unsubscribe() }) - test('should prefer isDataEqual to structuralSharing', async () => { - const key = queryKey() - - const data = { value: 'data' } - const newData = { value: 'data' } - - const observer = new QueryObserver(queryClient, { - queryKey: key, - queryFn: () => data, - }) - - const unsubscribe = observer.subscribe(() => undefined) - - await sleep(10) - expect(observer.getCurrentResult().data).toBe(data) - - observer.setOptions({ - queryKey: key, - queryFn: () => newData, - isDataEqual: () => true, - structuralSharing: false, - }) - - await observer.refetch() - expect(observer.getCurrentResult().data).toBe(data) - - unsubscribe() - }) - test('select function error using placeholderdata should log an error', () => { const key = queryKey() diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 720ea15f5b..16d538f982 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -70,7 +70,6 @@ export interface QueryOptions< retryDelay?: RetryDelayValue networkMode?: NetworkMode cacheTime?: number - isDataEqual?: (oldData: TData | undefined, newData: TData) => boolean queryFn?: QueryFunction queryHash?: string queryKey?: TQueryKey diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index d24543ffed..5adf603245 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -349,10 +349,7 @@ export function replaceData< TData, TOptions extends QueryOptions, >(prevData: TData | undefined, data: TData, options: TOptions): TData { - // Use prev data if an isDataEqual function is defined and returns `true` - if (options.isDataEqual?.(prevData, data)) { - return prevData as TData - } else if (typeof options.structuralSharing === 'function') { + if (typeof options.structuralSharing === 'function') { return options.structuralSharing(prevData, data) } else if (options.structuralSharing !== false) { // Structurally share data between prev and new data if needed From 474bc43a1f080d6dd6a8ed826fd0038910ca678b Mon Sep 17 00:00:00 2001 From: "Mahmoud M. Anwar" Date: Sat, 7 Jan 2023 13:50:54 +0200 Subject: [PATCH 008/314] feat(prefer-query-object-syntax): remove prefer-query-object-syntax rule (#4773) the rule is not needed anymore BREAKING CHANGE: prefer-query-object-syntax eslint rule is removed --- docs/config.json | 4 - docs/react/eslint/eslint-plugin-query.md | 1 - .../eslint/prefer-query-object-syntax.md | 62 --- .../guides/migrating-to-react-query-5.md | 5 +- examples/solid/simple/.eslintrc | 1 - .../src/configs/index.test.ts | 1 - .../eslint-plugin-query/src/rules/index.ts | 2 - .../prefer-query-object-syntax.test.ts | 363 ------------------ .../prefer-query-object-syntax.ts | 227 ----------- 9 files changed, 4 insertions(+), 662 deletions(-) delete mode 100644 docs/react/eslint/prefer-query-object-syntax.md delete mode 100644 packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts delete mode 100644 packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts diff --git a/docs/config.json b/docs/config.json index 96244a63b2..e335efc20f 100644 --- a/docs/config.json +++ b/docs/config.json @@ -294,10 +294,6 @@ { "label": "Exhaustive Deps", "to": "react/eslint/exhaustive-deps" - }, - { - "label": "Prefer object syntax", - "to": "react/eslint/prefer-query-object-syntax" } ] }, diff --git a/docs/react/eslint/eslint-plugin-query.md b/docs/react/eslint/eslint-plugin-query.md index c228b1bd92..2df1535310 100644 --- a/docs/react/eslint/eslint-plugin-query.md +++ b/docs/react/eslint/eslint-plugin-query.md @@ -33,7 +33,6 @@ Then configure the rules you want to use under the rules section: { "rules": { "@tanstack/query/exhaustive-deps": "error", - "@tanstack/query/prefer-query-object-syntax": "error" } } ``` diff --git a/docs/react/eslint/prefer-query-object-syntax.md b/docs/react/eslint/prefer-query-object-syntax.md deleted file mode 100644 index 8a53cdbb0a..0000000000 --- a/docs/react/eslint/prefer-query-object-syntax.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: prefer-query-object-syntax -title: Prefer object syntax for useQuery ---- - -You can use [`useQuery`](https://tanstack.com/query/v4/docs/reference/useQuery) in two different ways. - -Standard - -```tsx -useQuery(queryKey, queryFn?, options?) - -// or - -useQuery(options) -``` - -This rule prefers the second option, as it is more consistent with other React Query hooks, like `useQueries`. It will also be the only available option in a future major version. - -## Rule Details - -Examples of **incorrect** code for this rule: - -```js -/* eslint "@tanstack/query/prefer-query-object-syntax": "error" */ - -import { useQuery } from '@tanstack/react-query'; - -useQuery(queryKey, queryFn, { - onSuccess, -}); - -useQuery(queryKey, { - queryFn, - onSuccess, -}); -``` - -Examples of **correct** code for this rule: - -```js -import { useQuery } from '@tanstack/react-query'; - -useQuery({ - queryKey, - queryFn, - onSuccess, -}); -``` - -## When Not To Use It - -If you don't care about useQuery consistency, then you will not need this rule. - -## Attributes - -- [x] ✅ Recommended -- [x] 🔧 Fixable - -## Credits - -This rule was initially developed by [KubaJastrz](https://github.com/KubaJastrz) in [eslint-plugin-react-query](https://github.com/KubaJastrz/eslint-plugin-react-query). diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index c068c1cd9a..ef873362e2 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -9,7 +9,7 @@ v5 is a major version, so there are some breaking changes to be aware of: ### Supports a single signature, one object -useQuery and friends used to have many overloads in TypeScript - different ways how the function can be invoked. Not only this was tough to maintain, type wise, it also required a runtime check to see which type the first and the second parameter, to correctly create options. +useQuery and friends used to have many overloads in TypeScript - different ways how the function can be invoked. Not only this was tough to maintain, type wise, it also required a runtime check to see which type the first and the second parameter, to correctly create options. now we only support the object format. @@ -135,3 +135,6 @@ You can achieve the same functionality by passing a function to `structuralShari To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. +### eslint `prefer-query-object-syntax` rule is removed + +Since the only supported syntax now is the object syntax, this rule is no longer needed \ No newline at end of file diff --git a/examples/solid/simple/.eslintrc b/examples/solid/simple/.eslintrc index d8c452e40c..ea632a1553 100644 --- a/examples/solid/simple/.eslintrc +++ b/examples/solid/simple/.eslintrc @@ -7,6 +7,5 @@ "rules": { "react/react-in-jsx-scope": "off", "jsx-a11y/accessible-emoji": "off", - "@tanstack/query/prefer-query-object-syntax": "off" } } diff --git a/packages/eslint-plugin-query/src/configs/index.test.ts b/packages/eslint-plugin-query/src/configs/index.test.ts index 1a7c7aa974..a11be28fe5 100644 --- a/packages/eslint-plugin-query/src/configs/index.test.ts +++ b/packages/eslint-plugin-query/src/configs/index.test.ts @@ -10,7 +10,6 @@ describe('configs', () => { ], "rules": { "@tanstack/query/exhaustive-deps": "error", - "@tanstack/query/prefer-query-object-syntax": "error", }, }, } diff --git a/packages/eslint-plugin-query/src/rules/index.ts b/packages/eslint-plugin-query/src/rules/index.ts index bba8422ffb..eb0acbae7f 100644 --- a/packages/eslint-plugin-query/src/rules/index.ts +++ b/packages/eslint-plugin-query/src/rules/index.ts @@ -1,7 +1,5 @@ import * as exhaustiveDeps from './exhaustive-deps/exhaustive-deps.rule' -import * as preferObjectSyntax from './prefer-query-object-syntax/prefer-query-object-syntax' export const rules = { [exhaustiveDeps.name]: exhaustiveDeps.rule, - [preferObjectSyntax.name]: preferObjectSyntax.rule, } diff --git a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts b/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts deleted file mode 100644 index 19962c7fe6..0000000000 --- a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts +++ /dev/null @@ -1,363 +0,0 @@ -import { rule, name } from './prefer-query-object-syntax' -import { normalizeIndent } from '../../utils/test-utils' -import { ESLintUtils } from '@typescript-eslint/utils' - -const ruleTester = new ESLintUtils.RuleTester({ - parser: '@typescript-eslint/parser', - settings: {}, -}) - -ruleTester.run(name, rule, { - valid: [ - { - code: normalizeIndent` - useQuery() - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const result = useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const result = useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "somewhere-else"; - useQuery(queryKey, queryFn, { enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getPosts = async () => Promise.resolve([]); - const postsQuery = { queryKey: ["posts"], queryFn: () => getPosts() }; - const usePosts = () => useQuery(postsQuery); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => ({ queryKey: ['foo'], queryFn: () => Promise.resolve(5) }) - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - const queryKey = () => ['foo']; - const queryFn = () => { - return Promise.resolve(5); - } - return { queryKey, queryFn }; - } - useQuery(getQuery()) - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => { - try { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } finally { - return { queryKey: ['foo'], queryFn: () => Promise.resolve(5) }; - } - } - useQuery(getQuery()) - `, - }, - ], - - invalid: [ - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data']); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'] }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, queryFn); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - // no autofix - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], () => fetchData()); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData() }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, queryFn, { enabled }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], () => fetchData(), { enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(queryKey, { queryFn, enabled }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey, queryFn, enabled }); - `, - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = () => "foo"; - useQuery(getQuery()); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: '"foo"' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - return x - ? { queryKey: "foo", queryFn: () => Promise.resolve(1) } - : null; - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: `x\n ? { queryKey: "foo", queryFn: () => Promise.resolve(1) }\n : null`, - }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - try { - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - } catch (e) { - if (x > 1) { - return { queryKey: "bar", queryFn: () => Promise.resolve(2) }; - } else { - return null; - } - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: 'null' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x) => { - switch (x) { - case 1: - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - default: - return null; - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: 'null' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - const getQuery = (x, y) => { - if (x) { - return { queryKey: "foo", queryFn: () => Promise.resolve(1) }; - } else { - if (y) { - return { queryKey: "bar", queryFn: () => Promise.resolve(2) }; - } else { - return () => Promise.resolve(3); - } - } - }; - useQuery(getQuery(x)); - `, - errors: [ - { - messageId: 'returnTypeAreNotObjectSyntax', - data: { returnType: '() => Promise.resolve(3)' }, - }, - ], - }, - { - code: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery(['data'], { queryFn: () => fetchData(), enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { useQuery } from "@tanstack/react-query"; - useQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery(['data'], { queryFn: () => fetchData(), enabled: false }); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['data'], queryFn: () => fetchData(), enabled: false }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery( - ['key'], - () => Promise.resolve('data') - ); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery( - ['key'], () => Promise.resolve('data') - ); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery(['key'], () => Promise.resolve('data')); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery< - A, - B - >(['key'], () => Promise.resolve('data')); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - createQuery< - A, - B - >({ queryKey: ['key'], queryFn: () => Promise.resolve('data') }); - `, - }, - { - code: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const queryKeys = { userById: (userId: string) => ["users", {userId}] } - createQuery(queryKeys.userById(userId), async () => await fetchUserById(userId)); - `, - errors: [{ messageId: 'preferObjectSyntax' }], - output: normalizeIndent` - import { createQuery } from "@tanstack/solid-query"; - const queryKeys = { userById: (userId: string) => ["users", {userId}] } - createQuery({ queryKey: queryKeys.userById(userId), queryFn: async () => await fetchUserById(userId) }); - `, - }, - ], -}) diff --git a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts b/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts deleted file mode 100644 index 4e31aba47c..0000000000 --- a/packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts +++ /dev/null @@ -1,227 +0,0 @@ -import type { TSESLint, TSESTree } from '@typescript-eslint/utils' -import { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { createRule } from '../../utils/create-rule' -import { ASTUtils } from '../../utils/ast-utils' - -const QUERY_CALLS = ['useQuery', 'createQuery'] - -const messages = { - preferObjectSyntax: `Objects syntax for query is preferred`, - returnTypeAreNotObjectSyntax: `Return type of query should be object syntax. Got {{returnType}} instead`, -} - -type MessageKey = keyof typeof messages - -export const name = 'prefer-query-object-syntax' - -export const rule: TSESLint.RuleModule = - createRule({ - name, - meta: { - type: 'problem', - docs: { - description: 'Prefer object syntax for useQuery', - recommended: 'error', - }, - messages: messages, - fixable: 'code', - schema: [], - }, - defaultOptions: [], - - create(context, _, helpers) { - return { - CallExpression(node) { - const isTanstackQueryCall = - ASTUtils.isIdentifierWithOneOfNames(node.callee, QUERY_CALLS) && - helpers.isTanstackQueryImport(node.callee) - - if (!isTanstackQueryCall) { - return - } - - const firstArgument = node.arguments[0] - - if (!firstArgument) { - return - } - - if ( - node.arguments.length === 1 && - firstArgument.type === AST_NODE_TYPES.CallExpression - ) { - const referencedCallExpression = - ASTUtils.getReferencedExpressionByIdentifier({ - context, - node: firstArgument.callee, - }) - - if ( - referencedCallExpression === null || - !ASTUtils.isNodeOfOneOf(referencedCallExpression, [ - AST_NODE_TYPES.ArrowFunctionExpression, - AST_NODE_TYPES.FunctionExpression, - ]) - ) { - return - } - - if ( - !ASTUtils.isNodeOfOneOf(referencedCallExpression.body, [ - AST_NODE_TYPES.BlockStatement, - AST_NODE_TYPES.ObjectExpression, - ]) - ) { - return context.report({ - node, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: context - .getSourceCode() - .getText(referencedCallExpression.body), - }, - }) - } - - const returnStmts = ASTUtils.getNestedReturnStatements( - referencedCallExpression, - ) - - for (const stmt of returnStmts) { - if (stmt.argument === null) { - return context.report({ - node, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: 'void', - }, - }) - } - - runCheckOnNode({ - context: context, - callNode: node, - expression: stmt.argument, - messageId: 'returnTypeAreNotObjectSyntax', - }) - } - - return - } - - if (firstArgument.type === AST_NODE_TYPES.Identifier) { - const referencedNode = ASTUtils.getReferencedExpressionByIdentifier( - { - context, - node: firstArgument, - }, - ) - - if (referencedNode?.type === AST_NODE_TYPES.ObjectExpression) { - return runCheckOnNode({ - context: context, - callNode: node, - expression: referencedNode, - messageId: 'preferObjectSyntax', - }) - } - } - - runCheckOnNode({ - context: context, - callNode: node, - expression: firstArgument, - messageId: 'preferObjectSyntax', - }) - }, - } - }, - }) - -function runCheckOnNode(params: { - context: Readonly> - callNode: TSESTree.CallExpression - expression: TSESTree.Node - messageId: MessageKey -}) { - const { context, expression, messageId, callNode } = params - const sourceCode = context.getSourceCode() - - if (expression.type === AST_NODE_TYPES.ObjectExpression) { - return - } - - const secondArgument = callNode.arguments[1] - const thirdArgument = callNode.arguments[2] - - const optionsObject = - secondArgument?.type === AST_NODE_TYPES.ObjectExpression - ? secondArgument - : thirdArgument?.type === AST_NODE_TYPES.ObjectExpression - ? thirdArgument - : undefined - - if ( - secondArgument && - !thirdArgument && - secondArgument !== optionsObject && - secondArgument.type === AST_NODE_TYPES.Identifier - ) { - // Unable to determine if the secondArgument identifier is the options object or query fn. - // User has to fix the code manually. - context.report({ node: callNode, messageId: messageId }) - return - } - - if (messageId === 'returnTypeAreNotObjectSyntax') { - context.report({ - node: callNode, - messageId: 'returnTypeAreNotObjectSyntax', - data: { - returnType: sourceCode.getText(expression), - }, - }) - return - } - - context.report({ - node: callNode, - messageId: 'preferObjectSyntax', - fix(fixer) { - const optionsObjectProperties: string[] = [] - - // queryKey - const firstArgument = callNode.arguments[0] - const queryKey = sourceCode.getText(firstArgument) - const queryKeyProperty = - queryKey === 'queryKey' ? 'queryKey' : `queryKey: ${queryKey}` - - optionsObjectProperties.push(queryKeyProperty) - - // queryFn - if (secondArgument && secondArgument !== optionsObject) { - const queryFn = sourceCode.getText(secondArgument) - const queryFnProperty = - queryFn === 'queryFn' ? 'queryFn' : `queryFn: ${queryFn}` - - optionsObjectProperties.push(queryFnProperty) - } - - // options - if (optionsObject) { - const existingObjectProperties = optionsObject.properties.map( - (objectLiteral) => { - return sourceCode.getText(objectLiteral) - }, - ) - - optionsObjectProperties.push(...existingObjectProperties) - } - - const calleeText = sourceCode.getText(callNode).split('(')[0] - const argsText = `{ ${optionsObjectProperties.join(', ')} }` - - return fixer.replaceText(callNode, `${calleeText}(${argsText})`) - }, - }) -} From 3b45591f3e793626914c5c9f01ff79c46a6d74ae Mon Sep 17 00:00:00 2001 From: Thomas Ludlow Date: Sun, 8 Jan 2023 19:20:20 +0000 Subject: [PATCH 009/314] refactor: remove contextSharing (#4723) * docs(query-client-provider-reference): remove contextSharing reference remove contextSharing property reference as it is being removed * refactor(react-query): remove contextSharing remove the contextSharing property from the QueryClientProvider BREAKING CHANGE: contextSharing is removed from the QueryClientProvider * test(react-query): remove contextSharing remove contextSharing from QueryClientProvider tests * refactor(solid-query): remove contextSharing remove the contextSharing property from the QueryClientProvider BREAKING CHANGE: contextSharing is removed from the QueryClientProvider * test(solid-query): remove contextSharing remove contextSharing from QueryClientProvider tests * refactor(vue-query): remove contextSharing remove the contextSharing property from the ClientOptions BREAKING CHANGE: contextSharing is removed from the QueryClientProvider * test(vue-query): remove contextSharing remove contextSharing tests and functionality from the vueQueryPlugin tests * docs(migrating-to-react-query-5): document the removal of contextSharing * refactor(react-query): simplify QueryClientProviderProps type definition * refactor(solid-query) simplify QueryClientProviderProps type definition * refactor(react-query): rename QueryClientProvider context renaming this context then makes it match that of the solid-query implementation * refactor(solid-query): destructure query client provider props destructuring these props then makes it match the react-query implementation * revert: destructure query client provider props This reverts commit 96b5eb58662a371b3770cc43950b0f52e9505fee. * style(solid-query): fix imports after merge conflicts * style(v5-migration-guide): fix whitespace Co-authored-by: Dominik Dorfmeister --- .../guides/migrating-to-react-query-5.md | 7 +- docs/react/reference/QueryClientProvider.md | 4 - .../react-query/src/QueryClientProvider.tsx | 58 ++----------- .../__tests__/QueryClientProvider.test.tsx | 63 +------------- .../solid-query/src/QueryClientProvider.tsx | 82 +++---------------- .../__tests__/QueryClientProvider.test.tsx | 36 +------- .../src/__tests__/vueQueryPlugin.test.ts | 39 --------- packages/vue-query/src/vueQueryPlugin.ts | 34 +------- 8 files changed, 29 insertions(+), 294 deletions(-) diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index ef873362e2..134419119a 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -117,6 +117,11 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. +### The `contextSharing` prop has been removed from QueryClientProvider + +You could previously use the `contextSharing` property to share the first (and at least one) instance of the query client context across the window. This ensured that if TanStack Query was used across different bundles or microfrontends then they will all use the same instance of the context, regardless of module scoping. + +However, isolation is often preferred for microfrontends. In v4 the option to pass a custom context to the `QueryClientProvider` was added, which allows exactly this. If you wish to use the same query client across multiple packages of an application, you can create a `QueryClient` in your application and then let the bundles share this through the `context` property of the `QueryClientProvider`. ### The `isDataEqual` options has been removed from useQuery @@ -137,4 +142,4 @@ To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion ### eslint `prefer-query-object-syntax` rule is removed -Since the only supported syntax now is the object syntax, this rule is no longer needed \ No newline at end of file +Since the only supported syntax now is the object syntax, this rule is no longer needed diff --git a/docs/react/reference/QueryClientProvider.md b/docs/react/reference/QueryClientProvider.md index 616be1c4a0..f0be794d89 100644 --- a/docs/react/reference/QueryClientProvider.md +++ b/docs/react/reference/QueryClientProvider.md @@ -20,9 +20,5 @@ function App() { - `client: QueryClient` - **Required** - the QueryClient instance to provide -- `contextSharing: boolean` - - **Deprecated** - - defaults to `false` - - 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. - `context?: React.Context` - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. diff --git a/packages/react-query/src/QueryClientProvider.tsx b/packages/react-query/src/QueryClientProvider.tsx index 33a14f69cc..ec94ef003f 100644 --- a/packages/react-query/src/QueryClientProvider.tsx +++ b/packages/react-query/src/QueryClientProvider.tsx @@ -3,46 +3,22 @@ import * as React from 'react' import type { QueryClient } from '@tanstack/query-core' import type { ContextOptions } from './types' -declare global { - interface Window { - ReactQueryClientContext?: React.Context - } -} - export const defaultContext = React.createContext( undefined, ) -const QueryClientSharingContext = React.createContext(false) -// If we are given a context, we will use it. -// Otherwise, if contextSharing is on, we 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. function getQueryClientContext( context: React.Context | undefined, - contextSharing: boolean, ) { if (context) { return context } - if (contextSharing && typeof window !== 'undefined') { - if (!window.ReactQueryClientContext) { - window.ReactQueryClientContext = defaultContext - } - - return window.ReactQueryClientContext - } return defaultContext } export const useQueryClient = ({ context }: ContextOptions = {}) => { - const queryClient = React.useContext( - getQueryClientContext(context, React.useContext(QueryClientSharingContext)), - ) + const queryClient = React.useContext(getQueryClientContext(context)) if (!queryClient) { throw new Error('No QueryClient set, use QueryClientProvider to set one') @@ -51,27 +27,15 @@ export const useQueryClient = ({ context }: ContextOptions = {}) => { return queryClient } -type QueryClientProviderPropsBase = { +export type QueryClientProviderProps = { client: QueryClient children?: React.ReactNode -} -type QueryClientProviderPropsWithContext = ContextOptions & { - contextSharing?: never -} & QueryClientProviderPropsBase -type QueryClientProviderPropsWithContextSharing = { - context?: never - contextSharing?: boolean -} & QueryClientProviderPropsBase - -export type QueryClientProviderProps = - | QueryClientProviderPropsWithContext - | QueryClientProviderPropsWithContextSharing +} & ContextOptions export const QueryClientProvider = ({ client, children, context, - contextSharing = false, }: QueryClientProviderProps): JSX.Element => { React.useEffect(() => { client.mount() @@ -80,19 +44,11 @@ export const QueryClientProvider = ({ } }, [client]) - if (process.env.NODE_ENV !== 'production' && contextSharing) { - client - .getLogger() - .error( - `The contextSharing option has been deprecated and will be removed in the next major version`, - ) - } - - const Context = getQueryClientContext(context, contextSharing) + const QueryClientContext = getQueryClientContext(context) return ( - - {children} - + + {children} + ) } diff --git a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx index 1c67c74540..bc1474a520 100644 --- a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx @@ -1,6 +1,5 @@ import * as React from 'react' import { render, waitFor } from '@testing-library/react' -import { renderToString } from 'react-dom/server' import { sleep, queryKey, createQueryClient } from './utils' import { @@ -194,16 +193,10 @@ describe('QueryClientProvider', () => { ) } - // contextSharing should be ignored when passing a custom context. - const contextSharing = true - const rendered = render( - + @@ -233,59 +226,5 @@ describe('QueryClientProvider', () => { consoleMock.mockRestore() }) - - test('should use window to get the context when contextSharing is true', () => { - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - - let queryClientFromHook: QueryClient | undefined - let queryClientFromWindow: QueryClient | undefined - - function Page() { - queryClientFromHook = useQueryClient() - queryClientFromWindow = React.useContext( - window.ReactQueryClientContext as React.Context< - QueryClient | undefined - >, - ) - return null - } - - render( - - - , - ) - - expect(queryClientFromHook).toEqual(queryClient) - expect(queryClientFromWindow).toEqual(queryClient) - }) - - test('should not use window to get the context when contextSharing is true and window does not exist', () => { - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - - // Mock a non web browser environment - const windowSpy = jest - .spyOn(window, 'window', 'get') - .mockImplementation(undefined) - - let queryClientFromHook: QueryClient | undefined - - function Page() { - queryClientFromHook = useQueryClient() - return null - } - - renderToString( - - - , - ) - - expect(queryClientFromHook).toEqual(queryClient) - - windowSpy.mockRestore() - }) }) }) diff --git a/packages/solid-query/src/QueryClientProvider.tsx b/packages/solid-query/src/QueryClientProvider.tsx index 48688fe977..8063d7cd7d 100644 --- a/packages/solid-query/src/QueryClientProvider.tsx +++ b/packages/solid-query/src/QueryClientProvider.tsx @@ -1,52 +1,22 @@ import type { QueryClient } from '@tanstack/query-core' import type { Context, JSX } from 'solid-js' -import { - createContext, - useContext, - onMount, - onCleanup, - mergeProps, -} from 'solid-js' +import { createContext, useContext, onMount, onCleanup } from 'solid-js' import type { ContextOptions } from './types' -declare global { - interface Window { - SolidQueryClientContext?: Context - } -} - export const defaultContext = createContext(undefined) -const QueryClientSharingContext = createContext(false) -// If we are given a context, we will use it. -// Otherwise, if contextSharing is on, we share the first and at least one -// instance of the context across the window -// to ensure that if Solid Query is used across -// different bundles or microfrontends they will -// all use the same **instance** of context, regardless -// of module scoping. function getQueryClientContext( context: Context | undefined, - contextSharing: boolean, ) { if (context) { return context } - if (contextSharing && typeof window !== 'undefined') { - if (!window.SolidQueryClientContext) { - window.SolidQueryClientContext = defaultContext - } - - return window.SolidQueryClientContext - } return defaultContext } export const useQueryClient = ({ context }: ContextOptions = {}) => { - const queryClient = useContext( - getQueryClientContext(context, useContext(QueryClientSharingContext)), - ) + const queryClient = useContext(getQueryClientContext(context)) if (!queryClient) { throw new Error('No QueryClient set, use QueryClientProvider to set one') @@ -55,56 +25,24 @@ export const useQueryClient = ({ context }: ContextOptions = {}) => { return queryClient } -type QueryClientProviderPropsBase = { +export type QueryClientProviderProps = { client: QueryClient children?: JSX.Element -} -type QueryClientProviderPropsWithContext = ContextOptions & { - contextSharing?: never -} & QueryClientProviderPropsBase -type QueryClientProviderPropsWithContextSharing = { - context?: never - contextSharing?: boolean -} & QueryClientProviderPropsBase - -export type QueryClientProviderProps = - | QueryClientProviderPropsWithContext - | QueryClientProviderPropsWithContextSharing +} & ContextOptions export const QueryClientProvider = ( props: QueryClientProviderProps, ): JSX.Element => { - const mergedProps = mergeProps( - { - contextSharing: false, - }, - props, - ) onMount(() => { - mergedProps.client.mount() - - if (process.env.NODE_ENV !== 'production' && mergedProps.contextSharing) { - mergedProps.client - .getLogger() - .error( - `The contextSharing option has been deprecated and will be removed in the next major version`, - ) - } + props.client.mount() }) - onCleanup(() => mergedProps.client.unmount()) + onCleanup(() => props.client.unmount()) - const QueryClientContext = getQueryClientContext( - mergedProps.context, - mergedProps.contextSharing, - ) + const QueryClientContext = getQueryClientContext(props.context) return ( - - - {mergedProps.children} - - + + {props.children} + ) } diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index 5fde9d2751..d39848d0f2 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -2,8 +2,7 @@ import { render, screen, waitFor } from 'solid-testing-library' import { queryKey } from './utils' import { QueryCache, QueryClient } from '@tanstack/query-core' -import type { Context } from 'solid-js' -import { createContext, useContext } from 'solid-js' +import { createContext } from 'solid-js' import { createQuery, QueryClientProvider, useQueryClient } from '..' import { createQueryClient, sleep } from './utils' @@ -188,16 +187,10 @@ describe('QueryClientProvider', () => { ) } - // contextSharing should be ignored when passing a custom context. - const contextSharing = true - render(() => ( - + @@ -227,30 +220,5 @@ describe('QueryClientProvider', () => { consoleMock.mockRestore() }) - - it('should use window to get the context when contextSharing is true', () => { - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - - let queryClientFromHook: QueryClient | undefined - let queryClientFromWindow: QueryClient | undefined - - function Page() { - queryClientFromHook = useQueryClient() - queryClientFromWindow = useContext( - window.SolidQueryClientContext as Context, - ) - return null - } - - render(() => ( - - - - )) - - expect(queryClientFromHook).toEqual(queryClient) - expect(queryClientFromWindow).toEqual(queryClient) - }) }) }) diff --git a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts index 196222da57..5dfbbd9a80 100644 --- a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts +++ b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts @@ -41,10 +41,6 @@ function getAppMock(withUnmountHook = false): TestApp { } describe('VueQueryPlugin', () => { - beforeEach(() => { - window.__VUE_QUERY_CONTEXT__ = undefined - }) - describe('devtools', () => { test('should NOT setup devtools', () => { const setupDevtoolsMock = setupDevtools as jest.Mock @@ -229,41 +225,6 @@ describe('VueQueryPlugin', () => { ) }) - describe('when context sharing is enabled', () => { - test('should create context if it does not exist', () => { - const appMock = getAppMock() - VueQueryPlugin.install(appMock, { contextSharing: true }) - - expect(window.__VUE_QUERY_CONTEXT__).toBeTruthy() - }) - - test('should create context with options if it does not exist', () => { - const appMock = getAppMock() - VueQueryPlugin.install(appMock, { - contextSharing: true, - queryClientConfig: { defaultOptions: { queries: { staleTime: 5000 } } }, - }) - - expect( - window.__VUE_QUERY_CONTEXT__?.getDefaultOptions().queries?.staleTime, - ).toEqual(5000) - }) - - test('should use existing context', () => { - const customClient = { - mount: jest.fn(), - getLogger: () => ({ - error: jest.fn(), - }), - } as unknown as QueryClient - window.__VUE_QUERY_CONTEXT__ = customClient - const appMock = getAppMock() - VueQueryPlugin.install(appMock, { contextSharing: true }) - - expect(customClient.mount).toHaveBeenCalledTimes(1) - }) - }) - describe('when persister is provided', () => { test('should properly modify isRestoring flag on queryClient', async () => { const appMock = getAppMock() diff --git a/packages/vue-query/src/vueQueryPlugin.ts b/packages/vue-query/src/vueQueryPlugin.ts index 5d5478791f..666e7af202 100644 --- a/packages/vue-query/src/vueQueryPlugin.ts +++ b/packages/vue-query/src/vueQueryPlugin.ts @@ -6,17 +6,10 @@ import { getClientKey } from './utils' import { setupDevtools } from './devtools/devtools' import type { MaybeRefDeep } from './types' -declare global { - interface Window { - __VUE_QUERY_CONTEXT__?: QueryClient - } -} - type ClientPersister = (client: QueryClient) => [() => void, Promise] interface CommonOptions { queryClientKey?: string - contextSharing?: boolean clientPersister?: ClientPersister } @@ -38,22 +31,9 @@ export const VueQueryPlugin = { if ('queryClient' in options && options.queryClient) { client = options.queryClient } else { - if (options.contextSharing && typeof window !== 'undefined') { - if (!window.__VUE_QUERY_CONTEXT__) { - const clientConfig = - 'queryClientConfig' in options - ? options.queryClientConfig - : undefined - client = new QueryClient(clientConfig) - window.__VUE_QUERY_CONTEXT__ = client - } else { - client = window.__VUE_QUERY_CONTEXT__ - } - } else { - const clientConfig = - 'queryClientConfig' in options ? options.queryClientConfig : undefined - client = new QueryClient(clientConfig) - } + const clientConfig = + 'queryClientConfig' in options ? options.queryClientConfig : undefined + client = new QueryClient(clientConfig) } client.mount() @@ -70,14 +50,6 @@ export const VueQueryPlugin = { }) } - if (process.env.NODE_ENV !== 'production' && options.contextSharing) { - client - .getLogger() - .error( - `The contextSharing option has been deprecated and will be removed in the next major version`, - ) - } - const cleanup = () => { client.unmount() persisterUnmount() From 12659f54047563ca691a8518e06ea02a076084be Mon Sep 17 00:00:00 2001 From: Judicael <46365844+judicaelandria@users.noreply.github.com> Date: Sun, 8 Jan 2023 23:16:02 +0300 Subject: [PATCH 010/314] feat(TError) : Make TError default to Error instead of unknown (#4706) * feat: Make Ts Error default to Error * fix(suspense test): reject new error * remove extends value * refactor: revert some type assertions * fix type inference on query * fix TError type to default Error * fix formatting * test: fix suspense tests always Reject with Errors instead of plain strings * docs: upgrade guide for Error change Co-authored-by: Dominik Dorfmeister --- .../guides/migrating-to-react-query-5.md | 18 ++++++ .../query-core/src/infiniteQueryObserver.ts | 2 +- packages/query-core/src/mutation.ts | 4 +- packages/query-core/src/mutationCache.ts | 2 +- packages/query-core/src/mutationObserver.ts | 2 +- packages/query-core/src/query.ts | 6 +- packages/query-core/src/queryCache.ts | 4 +- packages/query-core/src/queryClient.ts | 20 +++--- packages/query-core/src/queryObserver.ts | 2 +- packages/query-core/src/retryer.ts | 8 +-- .../query-core/src/tests/mutations.test.tsx | 2 +- .../src/tests/queriesObserver.test.tsx | 2 +- packages/query-core/src/types.ts | 62 +++++++++---------- .../src/__tests__/suspense.test.tsx | 10 +-- .../src/__tests__/useQuery.test.tsx | 8 +-- packages/react-query/src/types.ts | 26 ++++---- packages/react-query/src/useInfiniteQuery.ts | 2 +- packages/react-query/src/useMutation.ts | 2 +- packages/react-query/src/useQueries.ts | 16 +++-- packages/react-query/src/useQuery.ts | 10 +-- .../src/__tests__/createQuery.test.tsx | 8 +-- .../src/__tests__/suspense.test.tsx | 10 +-- .../solid-query/src/createInfiniteQuery.ts | 2 +- packages/solid-query/src/createMutation.ts | 2 +- packages/solid-query/src/createQueries.ts | 16 +++-- packages/solid-query/src/createQuery.ts | 11 ++-- packages/solid-query/src/types.ts | 32 +++++----- .../__tests__/useInfiniteQuery.types.test.tsx | 2 +- .../src/__tests__/useMutation.types.test.tsx | 2 +- .../src/__tests__/useQuery.types.test.tsx | 2 +- packages/vue-query/src/mutationCache.ts | 2 +- packages/vue-query/src/queryCache.ts | 2 +- packages/vue-query/src/queryClient.ts | 16 ++--- packages/vue-query/src/types.ts | 4 +- packages/vue-query/src/useBaseQuery.ts | 2 +- packages/vue-query/src/useInfiniteQuery.ts | 4 +- packages/vue-query/src/useMutation.ts | 8 +-- packages/vue-query/src/useQueries.ts | 4 +- packages/vue-query/src/useQuery.ts | 12 ++-- 39 files changed, 181 insertions(+), 168 deletions(-) diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index 134419119a..a57341a479 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -140,6 +140,24 @@ You can achieve the same functionality by passing a function to `structuralShari To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. +### `Error` is now the default type for errors instead of `unknown` + +Even though in JavaScript, you can `throw` anything (which makes `unknown` the most correct type), almost always, `Errors` (or subclasses of `Error`) are thrown. This change makes it easier to work with the `error` field in TypeScript for most cases. + +If you want to throw something that isn't an Error, you'll now have to set the generic for yourself: + +```ts +useQuery({ + queryKey: ['some-query'], + queryFn: async () => { + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + } +}) +``` + ### eslint `prefer-query-object-syntax` rule is removed Since the only supported syntax now is the object syntax, this rule is no longer needed diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index 921f5dc8d2..e51533d92e 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -23,7 +23,7 @@ type InfiniteQueryObserverListener = ( export class InfiniteQueryObserver< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 7d3ee1bfb8..ea83d0b1b3 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -22,7 +22,7 @@ interface MutationConfig { export interface MutationState< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > { @@ -84,7 +84,7 @@ export type Action = export class Mutation< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends Removable { diff --git a/packages/query-core/src/mutationCache.ts b/packages/query-core/src/mutationCache.ts index acf87caf70..63cea3249f 100644 --- a/packages/query-core/src/mutationCache.ts +++ b/packages/query-core/src/mutationCache.ts @@ -130,7 +130,7 @@ export class MutationCache extends Subscribable { return this.mutations } - find( + find( filters: MutationFilters, ): Mutation | undefined { if (typeof filters.exact === 'undefined') { diff --git a/packages/query-core/src/mutationObserver.ts b/packages/query-core/src/mutationObserver.ts index 20f7e62da2..b392c127d6 100644 --- a/packages/query-core/src/mutationObserver.ts +++ b/packages/query-core/src/mutationObserver.ts @@ -27,7 +27,7 @@ interface NotifyOptions { export class MutationObserver< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends Subscribable< diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index fbbbc4c628..4931dea7f1 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -36,7 +36,7 @@ interface QueryConfig< state?: QueryState } -export interface QueryState { +export interface QueryState { data: TData | undefined dataUpdateCount: number dataUpdatedAt: number @@ -67,7 +67,7 @@ export interface FetchContext< export interface QueryBehavior< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > { @@ -140,7 +140,7 @@ export interface SetStateOptions { export class Query< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends Removable { diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index 78dad1b87d..ff9720e909 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -147,7 +147,7 @@ export class QueryCache extends Subscribable { get< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueyKey extends QueryKey = QueryKey, >( @@ -160,7 +160,7 @@ export class QueryCache extends Subscribable { return this.queries } - find( + find( filters: WithRequired, ): Query | undefined { if (typeof filters.exact === 'undefined') { diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 7888a22ac5..58033e7f80 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -187,7 +187,7 @@ export class QueryClient { ) } - getQueryState( + getQueryState( queryKey: QueryKey, ): QueryState | undefined { return this.queryCache.find({ queryKey })?.state @@ -286,7 +286,7 @@ export class QueryClient { fetchQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -308,7 +308,7 @@ export class QueryClient { prefetchQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -319,7 +319,7 @@ export class QueryClient { fetchInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -331,7 +331,7 @@ export class QueryClient { prefetchInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -455,11 +455,11 @@ export class QueryClient { } defaultQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey extends QueryKey, + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, >( options?: | QueryObserverOptions diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 96ccd0a378..0dcb1bbb2c 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -40,7 +40,7 @@ export interface ObserverFetchOptions extends FetchOptions { export class QueryObserver< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index f54c53f129..36325b2275 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -5,7 +5,7 @@ import type { CancelOptions, NetworkMode } from './types' // TYPES -interface RetryerConfig { +interface RetryerConfig { fn: () => TData | Promise abort?: () => void onError?: (error: TError) => void @@ -28,14 +28,14 @@ export interface Retryer { export type RetryValue = boolean | number | ShouldRetryFunction -type ShouldRetryFunction = ( +type ShouldRetryFunction = ( failureCount: number, error: TError, ) => boolean export type RetryDelayValue = number | RetryDelayFunction -type RetryDelayFunction = ( +type RetryDelayFunction = ( failureCount: number, error: TError, ) => number @@ -63,7 +63,7 @@ export function isCancelledError(value: any): value is CancelledError { return value instanceof CancelledError } -export function createRetryer( +export function createRetryer( config: RetryerConfig, ): Retryer { let isRetryCancelled = false diff --git a/packages/query-core/src/tests/mutations.test.tsx b/packages/query-core/src/tests/mutations.test.tsx index 5eced09b45..1d4604f912 100644 --- a/packages/query-core/src/tests/mutations.test.tsx +++ b/packages/query-core/src/tests/mutations.test.tsx @@ -325,7 +325,7 @@ describe('mutations', () => { context: undefined, variables: undefined, data: 'new', - error: undefined, + error: null, failureCount: 0, failureReason: null, isPaused: false, diff --git a/packages/query-core/src/tests/queriesObserver.test.tsx b/packages/query-core/src/tests/queriesObserver.test.tsx index e16c6d5de8..6f99192dc9 100644 --- a/packages/query-core/src/tests/queriesObserver.test.tsx +++ b/packages/query-core/src/tests/queriesObserver.test.tsx @@ -303,7 +303,7 @@ describe('queriesObserver', () => { const newQueryObserver = new QueryObserver< unknown, - unknown, + Error, unknown, unknown, QueryKey diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 16d538f982..0ff4b0f347 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -56,7 +56,7 @@ export type NetworkMode = 'online' | 'always' | 'offlineFirst' export interface QueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > { @@ -117,7 +117,7 @@ export type ThrowErrors< export interface QueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -248,7 +248,7 @@ export type WithRequired = Omit & Required> export type DefaultedQueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -259,7 +259,7 @@ export type DefaultedQueryObserverOptions< export interface InfiniteQueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -273,7 +273,7 @@ export interface InfiniteQueryObserverOptions< export type DefaultedInfiniteQueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -290,7 +290,7 @@ export type DefaultedInfiniteQueryObserverOptions< export interface FetchQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -306,7 +306,7 @@ export interface FetchQueryOptions< export interface FetchInfiniteQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends FetchQueryOptions< @@ -362,7 +362,7 @@ export interface FetchPreviousPageOptions extends ResultOptions { export type QueryStatus = 'loading' | 'error' | 'success' export type FetchStatus = 'fetching' | 'paused' | 'idle' -export interface QueryObserverBaseResult { +export interface QueryObserverBaseResult { data: TData | undefined dataUpdatedAt: number error: TError | null @@ -391,7 +391,7 @@ export interface QueryObserverBaseResult { fetchStatus: FetchStatus } -export interface QueryObserverLoadingResult +export interface QueryObserverLoadingResult extends QueryObserverBaseResult { data: undefined error: null @@ -405,7 +405,7 @@ export interface QueryObserverLoadingResult export interface QueryObserverLoadingErrorResult< TData = unknown, - TError = unknown, + TError = Error, > extends QueryObserverBaseResult { data: undefined error: TError @@ -419,7 +419,7 @@ export interface QueryObserverLoadingErrorResult< export interface QueryObserverRefetchErrorResult< TData = unknown, - TError = unknown, + TError = Error, > extends QueryObserverBaseResult { data: TData error: TError @@ -431,7 +431,7 @@ export interface QueryObserverRefetchErrorResult< status: 'error' } -export interface QueryObserverSuccessResult +export interface QueryObserverSuccessResult extends QueryObserverBaseResult { data: TData error: null @@ -443,18 +443,18 @@ export interface QueryObserverSuccessResult status: 'success' } -export type DefinedQueryObserverResult = +export type DefinedQueryObserverResult = | QueryObserverRefetchErrorResult | QueryObserverSuccessResult -export type QueryObserverResult = +export type QueryObserverResult = | DefinedQueryObserverResult | QueryObserverLoadingErrorResult | QueryObserverLoadingResult export interface InfiniteQueryObserverBaseResult< TData = unknown, - TError = unknown, + TError = Error, > extends QueryObserverBaseResult, TError> { fetchNextPage: ( options?: FetchNextPageOptions, @@ -470,7 +470,7 @@ export interface InfiniteQueryObserverBaseResult< export interface InfiniteQueryObserverLoadingResult< TData = unknown, - TError = unknown, + TError = Error, > extends InfiniteQueryObserverBaseResult { data: undefined error: null @@ -484,7 +484,7 @@ export interface InfiniteQueryObserverLoadingResult< export interface InfiniteQueryObserverLoadingErrorResult< TData = unknown, - TError = unknown, + TError = Error, > extends InfiniteQueryObserverBaseResult { data: undefined error: TError @@ -498,7 +498,7 @@ export interface InfiniteQueryObserverLoadingErrorResult< export interface InfiniteQueryObserverRefetchErrorResult< TData = unknown, - TError = unknown, + TError = Error, > extends InfiniteQueryObserverBaseResult { data: InfiniteData error: TError @@ -512,7 +512,7 @@ export interface InfiniteQueryObserverRefetchErrorResult< export interface InfiniteQueryObserverSuccessResult< TData = unknown, - TError = unknown, + TError = Error, > extends InfiniteQueryObserverBaseResult { data: InfiniteData error: null @@ -524,7 +524,7 @@ export interface InfiniteQueryObserverSuccessResult< status: 'success' } -export type InfiniteQueryObserverResult = +export type InfiniteQueryObserverResult = | InfiniteQueryObserverLoadingErrorResult | InfiniteQueryObserverLoadingResult | InfiniteQueryObserverRefetchErrorResult @@ -544,7 +544,7 @@ export type MutationFunction = ( export interface MutationOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > { @@ -580,7 +580,7 @@ export interface MutationOptions< export interface MutationObserverOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationOptions { @@ -589,7 +589,7 @@ export interface MutationObserverOptions< export interface MutateOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > { @@ -609,7 +609,7 @@ export interface MutateOptions< export type MutateFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = ( @@ -619,7 +619,7 @@ export type MutateFunction< export interface MutationObserverBaseResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationState { @@ -633,7 +633,7 @@ export interface MutationObserverBaseResult< export interface MutationObserverIdleResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -648,7 +648,7 @@ export interface MutationObserverIdleResult< export interface MutationObserverLoadingResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -663,7 +663,7 @@ export interface MutationObserverLoadingResult< export interface MutationObserverErrorResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -678,7 +678,7 @@ export interface MutationObserverErrorResult< export interface MutationObserverSuccessResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -693,7 +693,7 @@ export interface MutationObserverSuccessResult< export type MutationObserverResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = @@ -709,7 +709,7 @@ export interface QueryClientConfig { defaultOptions?: DefaultOptions } -export interface DefaultOptions { +export interface DefaultOptions { queries?: QueryObserverOptions mutations?: MutationObserverOptions } diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 73515801a6..88df4c4932 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -637,7 +637,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('rendered')) }) - it('should not throw errors to the error boundary when a throwErrors function returns true', async () => { + it('should throw errors to the error boundary when a throwErrors function returns true', async () => { const key = queryKey() function Page() { @@ -645,11 +645,11 @@ describe("useQuery's in Suspense mode", () => { queryKey: key, queryFn: async (): Promise => { await sleep(10) - return Promise.reject('Remote Error') + return Promise.reject(new Error('Remote Error')) }, retry: false, suspense: true, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', }) return
    rendered
    } @@ -684,11 +684,11 @@ describe("useQuery's in Suspense mode", () => { queryKey: key, queryFn: async (): Promise => { await sleep(10) - return Promise.reject('Local Error') + return Promise.reject(new Error('Local Error')) }, retry: false, suspense: true, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', }) return
    rendered
    } diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 737113c57c..dcb31aecfa 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -2955,18 +2955,18 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery({ + const { status, error } = useQuery({ queryKey: key, - queryFn: () => Promise.reject('Local Error'), + queryFn: () => Promise.reject(new Error('Local Error')), retry: false, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', }) return (

    {status}

    -

    {error}

    +

    {error?.message}

    ) } diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index d030b0a577..b98f6e5f92 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -24,7 +24,7 @@ export interface ContextOptions { export interface UseBaseQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -36,7 +36,7 @@ export interface UseBaseQueryOptions< export interface UseQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -46,7 +46,7 @@ export interface UseQueryOptions< export interface UseInfiniteQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -64,32 +64,32 @@ export interface UseInfiniteQueryOptions< export type UseBaseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = QueryObserverResult export type UseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = UseBaseQueryResult export type DefinedUseBaseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = DefinedQueryObserverResult export type DefinedUseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = DefinedUseBaseQueryResult export type UseInfiniteQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = InfiniteQueryObserverResult export interface UseMutationOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends ContextOptions, @@ -100,7 +100,7 @@ export interface UseMutationOptions< export type UseMutateFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = ( @@ -109,14 +109,14 @@ export type UseMutateFunction< export type UseMutateAsyncFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = MutateFunction export type UseBaseMutationResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = unknown, TContext = unknown, > = Override< @@ -126,7 +126,7 @@ export type UseBaseMutationResult< export type UseMutationResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = unknown, TContext = unknown, > = UseBaseMutationResult diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 18d574c746..532ba03e08 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -6,7 +6,7 @@ import { useBaseQuery } from './useBaseQuery' // HOOK export function useInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index c0f0f82520..c2b2d56020 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -13,7 +13,7 @@ import { shouldThrowError } from './utils' export function useMutation< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, >( diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index 9251ee7cbd..281b5dd187 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -23,7 +23,7 @@ import { // - `context` is omitted as it is passed as a root-level option to `useQueries` instead. type UseQueryOptionsForUseQueries< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = Omit, 'context'> @@ -55,14 +55,9 @@ type GetOptions = queryFn?: QueryFunction select: (data: any) => infer TData } - ? UseQueryOptionsForUseQueries + ? UseQueryOptionsForUseQueries : T extends { queryFn?: QueryFunction } - ? UseQueryOptionsForUseQueries< - TQueryFnData, - unknown, - TQueryFnData, - TQueryKey - > + ? UseQueryOptionsForUseQueries : // Fallback UseQueryOptionsForUseQueries @@ -143,7 +138,10 @@ export type QueriesResults< any >[] ? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results - UseQueryResult[] + UseQueryResult< + unknown extends TData ? TQueryFnData : TData, + unknown extends TError ? Error : TError + >[] : // Fallback UseQueryResult[] diff --git a/packages/react-query/src/useQuery.ts b/packages/react-query/src/useQuery.ts index 77904ef528..a641351234 100644 --- a/packages/react-query/src/useQuery.ts +++ b/packages/react-query/src/useQuery.ts @@ -10,7 +10,7 @@ import { useBaseQuery } from './useBaseQuery' // HOOK type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -19,7 +19,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -28,7 +28,7 @@ type DefinedInitialDataOptions< export function useQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -37,7 +37,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -46,7 +46,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >(options: UseQueryOptions) { diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 4e2b1e7c3c..5e708a97d9 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -2856,17 +2856,17 @@ describe('createQuery', () => { const key = queryKey() function Page() { - const state = createQuery(() => ({ + const state = createQuery(() => ({ queryKey: key, - queryFn: () => Promise.reject('Local Error'), + queryFn: () => Promise.reject(new Error('Local Error')), retry: false, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', })) return (

    {state.status}

    -

    {state.error}

    +

    {state.error?.message}

    ) } diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index d9b3d588a6..0c7ce32a1b 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -631,7 +631,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('rendered')) }) - it('should not throw errors to the error boundary when a throwErrors function returns true', async () => { + it('should throw errors to the error boundary when a throwErrors function returns true', async () => { const key = queryKey() function Page() { @@ -639,10 +639,10 @@ describe("useQuery's in Suspense mode", () => { queryKey: key, queryFn: async (): Promise => { await sleep(10) - return Promise.reject('Remote Error') + return Promise.reject(new Error('Remote Error')) }, retry: false, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', })) // read state.data to trigger suspense. @@ -688,12 +688,12 @@ describe("useQuery's in Suspense mode", () => { queryKey: key, queryFn: async (): Promise => { await sleep(10) - return Promise.reject('Local Error') + return Promise.reject(new Error('Local Error')) }, retry: false, suspense: true, - throwErrors: (err) => err !== 'Local Error', + throwErrors: (err) => err.message !== 'Local Error', })) // read state.data to trigger suspense. diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts index 7250865ab2..a6e91f5e61 100644 --- a/packages/solid-query/src/createInfiniteQuery.ts +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -9,7 +9,7 @@ import { createMemo } from 'solid-js' export function createInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index 868ec62bb1..83b9935376 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -12,7 +12,7 @@ import { shouldThrowError } from './utils' // HOOK export function createMutation< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, >( diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index f5258799ea..d245bdcc1a 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -9,7 +9,7 @@ import type { CreateQueryResult, SolidQueryOptions } from './types' // - `context` is omitted as it is passed as a root-level option to `useQueries` instead. type CreateQueryOptionsForCreateQueries< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = Omit, 'context'> @@ -41,16 +41,11 @@ type GetOptions = queryFn?: QueryFunction select: (data: any) => infer TData } - ? CreateQueryOptionsForCreateQueries< - TQueryFnData, - unknown, - TData, - TQueryKey - > + ? CreateQueryOptionsForCreateQueries : T extends { queryFn?: QueryFunction } ? CreateQueryOptionsForCreateQueries< TQueryFnData, - unknown, + Error, TQueryFnData, TQueryKey > @@ -134,7 +129,10 @@ export type QueriesResults< any >[] ? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results - CreateQueryResult[] + CreateQueryResult< + unknown extends TData ? TQueryFnData : TData, + unknown extends TError ? Error : TError + >[] : // Fallback CreateQueryResult[] diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts index c264b7a2b2..d6a0c69538 100644 --- a/packages/solid-query/src/createQuery.ts +++ b/packages/solid-query/src/createQuery.ts @@ -12,7 +12,7 @@ import type { type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -23,7 +23,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -34,7 +34,7 @@ type DefinedInitialDataOptions< export function createQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -43,16 +43,15 @@ export function createQuery< export function createQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( options: DefinedInitialDataOptions, ): DefinedCreateQueryResult - export function createQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >(options: CreateQueryOptions) { diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index f3fffa3c71..4613945acc 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -26,7 +26,7 @@ export type FunctionedParams = () => T export interface CreateBaseQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -38,7 +38,7 @@ export interface CreateBaseQueryOptions< export interface SolidQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -54,7 +54,7 @@ export interface SolidQueryOptions< export type CreateQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams> @@ -63,28 +63,28 @@ export type CreateQueryOptions< export type CreateBaseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = QueryObserverResult export type CreateQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = CreateBaseQueryResult export type DefinedCreateBaseQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = DefinedQueryObserverResult export type DefinedCreateQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = DefinedCreateBaseQueryResult /* --- Create Infinite Queries Types --- */ export interface SolidInfiniteQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -104,7 +104,7 @@ export interface SolidInfiniteQueryOptions< export type CreateInfiniteQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -119,13 +119,13 @@ export type CreateInfiniteQueryOptions< export type CreateInfiniteQueryResult< TData = unknown, - TError = unknown, + TError = Error, > = InfiniteQueryObserverResult /* --- Create Mutation Types --- */ export interface SolidMutationOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > extends ContextOptions, @@ -136,14 +136,14 @@ export interface SolidMutationOptions< export type CreateMutationOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = FunctionedParams> export type CreateMutateFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = ( @@ -152,14 +152,14 @@ export type CreateMutateFunction< export type CreateMutateAsyncFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = MutateFunction export type CreateBaseMutationResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = unknown, TContext = unknown, > = Override< @@ -171,7 +171,7 @@ export type CreateBaseMutationResult< export type CreateMutationResult< TData = unknown, - TError = unknown, + TError = Error, TVariables = unknown, TContext = unknown, > = CreateBaseMutationResult diff --git a/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx b/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx index 7e33a28ed7..a965a6c334 100644 --- a/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx +++ b/packages/vue-query/src/__tests__/useInfiniteQuery.types.test.tsx @@ -82,7 +82,7 @@ describe('Discriminated union return type', () => { ) if (query.isError) { - const result: Expect> = true + const result: Expect> = true return result } return diff --git a/packages/vue-query/src/__tests__/useMutation.types.test.tsx b/packages/vue-query/src/__tests__/useMutation.types.test.tsx index 5088180eec..3d9ee11c80 100644 --- a/packages/vue-query/src/__tests__/useMutation.types.test.tsx +++ b/packages/vue-query/src/__tests__/useMutation.types.test.tsx @@ -64,7 +64,7 @@ describe('Discriminated union return type', () => { ) if (mutation.isError) { - const result: Expect> = true + const result: Expect> = true return result } return diff --git a/packages/vue-query/src/__tests__/useQuery.types.test.tsx b/packages/vue-query/src/__tests__/useQuery.types.test.tsx index d0351a2ab4..e77516a6df 100644 --- a/packages/vue-query/src/__tests__/useQuery.types.test.tsx +++ b/packages/vue-query/src/__tests__/useQuery.types.test.tsx @@ -78,7 +78,7 @@ describe('Discriminated union return type', () => { ) if (query.isError) { - const result: Expect> = true + const result: Expect> = true return result } return diff --git a/packages/vue-query/src/mutationCache.ts b/packages/vue-query/src/mutationCache.ts index d12edd522f..f0bce63b71 100644 --- a/packages/vue-query/src/mutationCache.ts +++ b/packages/vue-query/src/mutationCache.ts @@ -4,7 +4,7 @@ import type { MaybeRefDeep } from './types' import { cloneDeepUnref } from './utils' export class MutationCache extends MC { - find( + find( filters: MaybeRefDeep, ): Mutation | undefined { return super.find(cloneDeepUnref(filters) as MutationFilters) diff --git a/packages/vue-query/src/queryCache.ts b/packages/vue-query/src/queryCache.ts index 3012ad92f9..35e2c51427 100644 --- a/packages/vue-query/src/queryCache.ts +++ b/packages/vue-query/src/queryCache.ts @@ -4,7 +4,7 @@ import type { MaybeRefDeep } from './types' import { cloneDeepUnref } from './utils' export class QueryCache extends QC { - find( + find( filters: MaybeRefDeep>, ): Query | undefined { const filtersUnreffed = cloneDeepUnref(filters) as WithRequired< diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index d796ccf813..6c4682a9b4 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -87,7 +87,7 @@ export class QueryClient extends QC { return super.setQueriesData(filtersUnreffed, updater, optionsUnreffed) } - getQueryState( + getQueryState( queryKey: MaybeRefDeep, ): QueryState | undefined { return super.getQueryState(cloneDeepUnref(queryKey) as QueryKey) @@ -143,7 +143,7 @@ export class QueryClient extends QC { fetchQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -171,7 +171,7 @@ export class QueryClient extends QC { prefetchQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -179,7 +179,7 @@ export class QueryClient extends QC { ): Promise prefetchQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -198,7 +198,7 @@ export class QueryClient extends QC { fetchInfiniteQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -206,7 +206,7 @@ export class QueryClient extends QC { ): Promise> fetchInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -223,7 +223,7 @@ export class QueryClient extends QC { prefetchInfiniteQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -231,7 +231,7 @@ export class QueryClient extends QC { ): Promise prefetchInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/vue-query/src/types.ts b/packages/vue-query/src/types.ts index 9c0e0d473d..9ae4b3ff8c 100644 --- a/packages/vue-query/src/types.ts +++ b/packages/vue-query/src/types.ts @@ -29,7 +29,7 @@ export type WithQueryClientKey = T & { // Accept refs as options export type VueQueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -63,7 +63,7 @@ export type VueQueryObserverOptions< // Accept refs as options export type VueInfiniteQueryObserverOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = unknown, TQueryData = unknown, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/vue-query/src/useBaseQuery.ts b/packages/vue-query/src/useBaseQuery.ts index 1d3b287378..2c8d5088d0 100644 --- a/packages/vue-query/src/useBaseQuery.ts +++ b/packages/vue-query/src/useBaseQuery.ts @@ -136,7 +136,7 @@ export function useBaseQuery< export function unrefQueryArgs< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index ea590fbf7c..e253d37498 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -17,7 +17,7 @@ import type { export type UseInfiniteQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = WithRequired< @@ -52,7 +52,7 @@ export type UseInfiniteQueryReturnType = DistributiveOmit< export function useInfiniteQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index 1337b516d2..0bc892de35 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -36,7 +36,7 @@ export type UseMutationOptions = export type VueMutationObserverOptions< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = { @@ -52,7 +52,7 @@ export type VueMutationObserverOptions< type MutateSyncFunction< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, > = ( @@ -73,7 +73,7 @@ export type UseMutationReturnType< export function useMutation< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, >( @@ -131,7 +131,7 @@ export function useMutation< export function unrefMutationArgs< TData = unknown, - TError = unknown, + TError = Error, TVariables = void, TContext = unknown, >( diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index 31e5ed1710..ab40ebcb09 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -44,9 +44,9 @@ type GetOptions = queryFn?: QueryFunction select: (data: any) => infer TData } - ? UseQueryOptions + ? UseQueryOptions : T extends { queryFn?: QueryFunction } - ? UseQueryOptions + ? UseQueryOptions : // Fallback UseQueryOptions diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index 42dff8aa62..024c5c97a5 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -31,7 +31,7 @@ export type UseQueryDefinedReturnType = DistributiveOmit< export type UseQueryOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = WithRequired< @@ -49,7 +49,7 @@ export type UseQueryOptions< type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -58,7 +58,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -67,7 +67,7 @@ type DefinedInitialDataOptions< export function useQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -76,7 +76,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = unknown, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -85,7 +85,7 @@ export function useQuery< export function useQuery< TQueryFnData, - TError, + TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( From fabc5d73c75587a046e89d181205f649c7e7d845 Mon Sep 17 00:00:00 2001 From: Gaurav Singh <24932097+illuzan@users.noreply.github.com> Date: Tue, 10 Jan 2023 16:06:08 +0530 Subject: [PATCH 011/314] chore(react-query): drop support for React17 (#4698) * chore(react-query): drop support for React17 * chore(test): cleanup jest setup * style(test): fix hydration formatting --- .github/workflows/pr.yml | 17 ---- package.json | 4 - packages/react-query-devtools/jest.setup.ts | 32 ------- packages/react-query-devtools/package.json | 16 ++-- .../react-query-devtools/src/devtools.tsx | 3 +- .../src/useSyncExternalStore.native.ts | 3 - .../src/useSyncExternalStore.ts | 2 - .../react-query-persist-client/jest.setup.ts | 34 +------ .../react-query-persist-client/package.json | 6 +- packages/react-query/jest.setup.ts | 32 ------- packages/react-query/package.json | 12 +-- .../src/__tests__/ssr-hydration.test.tsx | 30 ++----- .../src/__tests__/useQuery.test.tsx | 4 +- packages/react-query/src/useBaseQuery.ts | 3 +- packages/react-query/src/useIsFetching.ts | 3 +- packages/react-query/src/useIsMutating.ts | 3 +- packages/react-query/src/useMutation.ts | 3 +- packages/react-query/src/useQueries.ts | 3 +- .../src/useSyncExternalStore.native.ts | 5 -- .../react-query/src/useSyncExternalStore.ts | 4 - pnpm-lock.yaml | 88 +------------------ rollup.config.ts | 10 --- 22 files changed, 29 insertions(+), 288 deletions(-) delete mode 100644 packages/react-query-devtools/src/useSyncExternalStore.native.ts delete mode 100644 packages/react-query-devtools/src/useSyncExternalStore.ts delete mode 100644 packages/react-query/src/useSyncExternalStore.native.ts delete mode 100644 packages/react-query/src/useSyncExternalStore.ts diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a515a076dc..09c889b080 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -63,23 +63,6 @@ jobs: - name: Install dependencies run: pnpm --filter "./packages/**" --filter query --prefer-offline install - run: pnpm run test:format - test-react-17: - name: 'Test React 17' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.2 - with: - version: 7 - - uses: actions/setup-node@v3 - with: - node-version: 16.14.2 - cache: 'pnpm' - - name: Install dependencies - run: pnpm --filter "./packages/**" --filter query --prefer-offline install - - run: pnpm run test:react:17 - env: - REACTJS_VERSION: 17 test-size: name: 'Size' runs-on: ubuntu-latest diff --git a/package.json b/package.json index 6448b1dc44..b5022b4927 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "install:csb": "pnpm install --frozen-lockfile", "test": "pnpm run test:ci", "test:ci": "pnpm run test:format && pnpm run test:eslint && pnpm run test:jest --collectCoverage false && pnpm run typecheck", - "test:react:17": "pnpm --filter \"./packages/react-*\" --parallel --no-bail run test:jest --collectCoverage false", "test:eslint": "pnpm --filter \"./packages/**\" --parallel --no-bail run test:eslint", "test:format": "pnpm run prettier --check", "test:jest": "pnpm --filter \"./packages/**\" --parallel --no-bail run test:jest", @@ -35,7 +34,6 @@ "@rollup/plugin-replace": "^4.0.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", - "@testing-library/react-17": "npm:@testing-library/react@12.1.4", "@testing-library/react-hooks": "^7.0.2", "@tsconfig/svelte": "^3.0.0", "@types/jest": "^26.0.4", @@ -75,9 +73,7 @@ "luxon": "^2.3.2", "prettier": "^2.6.2", "react": "^18.2.0", - "react-17": "npm:react@^17.0.2", "react-dom": "^18.2.0", - "react-dom-17": "npm:react-dom@^17.0.2", "rollup": "^2.70.2", "rollup-plugin-size": "^0.2.2", "rollup-plugin-svelte": "^7.1.0", diff --git a/packages/react-query-devtools/jest.setup.ts b/packages/react-query-devtools/jest.setup.ts index 66937172d7..4bc5d7f0d3 100644 --- a/packages/react-query-devtools/jest.setup.ts +++ b/packages/react-query-devtools/jest.setup.ts @@ -5,35 +5,3 @@ import { notifyManager } from '@tanstack/query-core' notifyManager.setNotifyFunction((fn) => { act(fn) }) - -type ReactVersion = '18' | '17' - -jest.mock('react', () => { - const packages = { - '18': 'react', - '17': 'react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]!) -}) - -jest.mock('react-dom', () => { - const packages = { - '18': 'react-dom', - '17': 'react-dom-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) - -jest.mock('@testing-library/react', () => { - const packages = { - '18': '@testing-library/react', - '17': '@testing-library/react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) diff --git a/packages/react-query-devtools/package.json b/packages/react-query-devtools/package.json index e1817f1e03..2c7c553c40 100644 --- a/packages/react-query-devtools/package.json +++ b/packages/react-query-devtools/package.json @@ -44,24 +44,20 @@ "test:dev": "pnpm run test:jest --watch" }, "devDependencies": { + "@tanstack/react-query": "workspace:*", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", - "@types/use-sync-external-store": "^0.0.3", "react": "^18.2.0", - "react-17": "npm:react@^17.0.2", "react-dom": "^18.2.0", - "react-dom-17": "npm:react-dom@^17.0.2", - "react-error-boundary": "^3.1.4", - "@tanstack/react-query": "workspace:*" + "react-error-boundary": "^3.1.4" }, "dependencies": { "@tanstack/match-sorter-utils": "^8.7.0", - "superjson": "^1.10.0", - "use-sync-external-store": "^1.2.0" + "superjson": "^1.10.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "@tanstack/react-query": "workspace:*" + "@tanstack/react-query": "workspace:*", + "react": "^18.0.0", + "react-dom": "^18.0.0" } } diff --git a/packages/react-query-devtools/src/devtools.tsx b/packages/react-query-devtools/src/devtools.tsx index 695fa3dab3..8067984722 100644 --- a/packages/react-query-devtools/src/devtools.tsx +++ b/packages/react-query-devtools/src/devtools.tsx @@ -1,5 +1,4 @@ import * as React from 'react' -import { useSyncExternalStore } from './useSyncExternalStore' import type { QueryCache, QueryClient, @@ -402,7 +401,7 @@ const useSubscribeToQueryCache = ( getSnapshot: () => T, skip: boolean = false, ): T => { - return useSyncExternalStore( + return React.useSyncExternalStore( React.useCallback( (onStoreChange) => { if (!skip) diff --git a/packages/react-query-devtools/src/useSyncExternalStore.native.ts b/packages/react-query-devtools/src/useSyncExternalStore.native.ts deleted file mode 100644 index 226ccef519..0000000000 --- a/packages/react-query-devtools/src/useSyncExternalStore.native.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Temporary workaround due to an issue with react-native uSES - https://github.com/TanStack/query/pull/3601 -// @ts-ignore -export { useSyncExternalStore } from 'use-sync-external-store/shim/index.native.js' diff --git a/packages/react-query-devtools/src/useSyncExternalStore.ts b/packages/react-query-devtools/src/useSyncExternalStore.ts deleted file mode 100644 index c2b6d1454e..0000000000 --- a/packages/react-query-devtools/src/useSyncExternalStore.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Temporary workaround due to an issue with react-native uSES - https://github.com/TanStack/query/pull/3601 -export { useSyncExternalStore } from 'use-sync-external-store/shim/index.js' diff --git a/packages/react-query-persist-client/jest.setup.ts b/packages/react-query-persist-client/jest.setup.ts index c8dc25be2c..21dfe80c07 100644 --- a/packages/react-query-persist-client/jest.setup.ts +++ b/packages/react-query-persist-client/jest.setup.ts @@ -4,36 +4,4 @@ import { notifyManager } from '@tanstack/react-query' // Wrap notifications with act to make sure React knows about React Query updates notifyManager.setNotifyFunction((fn) => { act(fn) -}) - -type ReactVersion = '18' | '17' - -jest.mock('react', () => { - const packages = { - '18': 'react', - '17': 'react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]!) -}) - -jest.mock('react-dom', () => { - const packages = { - '18': 'react-dom', - '17': 'react-dom-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) - -jest.mock('@testing-library/react', () => { - const packages = { - '18': '@testing-library/react', - '17': '@testing-library/react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) +}) \ No newline at end of file diff --git a/packages/react-query-persist-client/package.json b/packages/react-query-persist-client/package.json index 1037daa875..171eb07f52 100644 --- a/packages/react-query-persist-client/package.json +++ b/packages/react-query-persist-client/package.json @@ -34,13 +34,11 @@ "test:dev": "pnpm run test:jest --watch" }, "devDependencies": { + "@tanstack/react-query": "workspace:*", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", "react": "^18.2.0", - "react-17": "npm:react@^17.0.2", - "react-dom": "^18.2.0", - "react-dom-17": "npm:react-dom@^17.0.2", - "@tanstack/react-query": "workspace:*" + "react-dom": "^18.2.0" }, "dependencies": { "@tanstack/query-persist-client-core": "workspace:*" diff --git a/packages/react-query/jest.setup.ts b/packages/react-query/jest.setup.ts index 66937172d7..4bc5d7f0d3 100644 --- a/packages/react-query/jest.setup.ts +++ b/packages/react-query/jest.setup.ts @@ -5,35 +5,3 @@ import { notifyManager } from '@tanstack/query-core' notifyManager.setNotifyFunction((fn) => { act(fn) }) - -type ReactVersion = '18' | '17' - -jest.mock('react', () => { - const packages = { - '18': 'react', - '17': 'react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]!) -}) - -jest.mock('react-dom', () => { - const packages = { - '18': 'react-dom', - '17': 'react-dom-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) - -jest.mock('@testing-library/react', () => { - const packages = { - '18': '@testing-library/react', - '17': '@testing-library/react-17', - } - const version = (process.env.REACTJS_VERSION || '18') as ReactVersion - - return jest.requireActual(packages[version]) -}) diff --git a/packages/react-query/package.json b/packages/react-query/package.json index 6358993031..4b572b33e7 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -44,21 +44,17 @@ "@types/jscodeshift": "^0.11.3", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", - "@types/use-sync-external-store": "^0.0.3", + "jscodeshift": "^0.13.1", "react": "^18.2.0", - "react-17": "npm:react@^17.0.2", "react-dom": "^18.2.0", - "react-dom-17": "npm:react-dom@^17.0.2", - "jscodeshift": "^0.13.1", "react-error-boundary": "^3.1.4" }, "dependencies": { - "@tanstack/query-core": "workspace:*", - "use-sync-external-store": "^1.2.0" + "@tanstack/query-core": "workspace:*" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", "react-native": "*" }, "peerDependenciesMeta": { diff --git a/packages/react-query/src/__tests__/ssr-hydration.test.tsx b/packages/react-query/src/__tests__/ssr-hydration.test.tsx index 0ad1d3c730..4eda208108 100644 --- a/packages/react-query/src/__tests__/ssr-hydration.test.tsx +++ b/packages/react-query/src/__tests__/ssr-hydration.test.tsx @@ -14,23 +14,14 @@ import { } from '..' import { createQueryClient, setIsServer, sleep } from './utils' -const isReact18 = () => (process.env.REACTJS_VERSION || '18') === '18' - const ReactHydrate = (element: React.ReactElement, container: Element) => { - if (isReact18()) { - let root: any - ReactDOMTestUtils.act(() => { - // @ts-expect-error - root = ReactDOM.hydrateRoot(container, element) - }) - return () => { - root.unmount() - } - } - - ReactDOM.hydrate(element, container) + let root: any + ReactDOMTestUtils.act(() => { + // @ts-expect-error + root = ReactDOM.hydrateRoot(container, element) + }) return () => { - ReactDOM.unmountComponentAtNode(container) + root.unmount() } } @@ -58,9 +49,6 @@ describe('Server side rendering with de/rehydration', () => { const consoleMock = jest.spyOn(console, 'error') consoleMock.mockImplementation(() => undefined) - if (!isReact18()) { - return - } const fetchDataSuccess = jest.fn< ReturnType, Parameters @@ -142,9 +130,6 @@ describe('Server side rendering with de/rehydration', () => { const consoleMock = jest.spyOn(console, 'error') consoleMock.mockImplementation(() => undefined) - if (!isReact18()) { - return - } const fetchDataError = jest.fn(() => { throw new Error('fetchDataError') }) @@ -225,9 +210,6 @@ describe('Server side rendering with de/rehydration', () => { const consoleMock = jest.spyOn(console, 'error') consoleMock.mockImplementation(() => undefined) - if (!isReact18()) { - return - } const fetchDataSuccess = jest.fn< ReturnType, Parameters diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index dcb31aecfa..c1241d2db8 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -2472,8 +2472,8 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('count: 2')) - // Should be 2 / 3 instead of 5, uSES batches differently - expect(renders).toBe(process.env.REACTJS_VERSION === '17' ? 2 : 3) + // Should be 3 instead of 5 + expect(renders).toBe(3) // Both callbacks should have been executed expect(callbackCount).toBe(2) diff --git a/packages/react-query/src/useBaseQuery.ts b/packages/react-query/src/useBaseQuery.ts index a3b201e1cd..449898056d 100644 --- a/packages/react-query/src/useBaseQuery.ts +++ b/packages/react-query/src/useBaseQuery.ts @@ -1,5 +1,4 @@ import * as React from 'react' -import { useSyncExternalStore } from './useSyncExternalStore' import type { QueryKey, QueryObserver } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' @@ -74,7 +73,7 @@ export function useBaseQuery< const result = observer.getOptimisticResult(defaultedOptions) - useSyncExternalStore( + React.useSyncExternalStore( React.useCallback( (onStoreChange) => isRestoring diff --git a/packages/react-query/src/useIsFetching.ts b/packages/react-query/src/useIsFetching.ts index 8f80b58ed1..08917a20a5 100644 --- a/packages/react-query/src/useIsFetching.ts +++ b/packages/react-query/src/useIsFetching.ts @@ -2,7 +2,6 @@ import * as React from 'react' import type { QueryFilters } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' -import { useSyncExternalStore } from './useSyncExternalStore' import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' @@ -15,7 +14,7 @@ export function useIsFetching( const queryClient = useQueryClient({ context: options.context }) const queryCache = queryClient.getQueryCache() - return useSyncExternalStore( + return React.useSyncExternalStore( React.useCallback( (onStoreChange) => queryCache.subscribe(notifyManager.batchCalls(onStoreChange)), diff --git a/packages/react-query/src/useIsMutating.ts b/packages/react-query/src/useIsMutating.ts index 7e95b58040..3c70eba3cf 100644 --- a/packages/react-query/src/useIsMutating.ts +++ b/packages/react-query/src/useIsMutating.ts @@ -1,5 +1,4 @@ import * as React from 'react' -import { useSyncExternalStore } from './useSyncExternalStore' import type { MutationFilters } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' @@ -15,7 +14,7 @@ export function useIsMutating( const queryClient = useQueryClient({ context: options.context }) const mutationCache = queryClient.getMutationCache() - return useSyncExternalStore( + return React.useSyncExternalStore( React.useCallback( (onStoreChange) => mutationCache.subscribe(notifyManager.batchCalls(onStoreChange)), diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index c2b2d56020..538acc7ef9 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -1,5 +1,4 @@ import * as React from 'react' -import { useSyncExternalStore } from './useSyncExternalStore' import { notifyManager, MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { @@ -33,7 +32,7 @@ export function useMutation< observer.setOptions(options) }, [observer, options]) - const result = useSyncExternalStore( + const result = React.useSyncExternalStore( React.useCallback( (onStoreChange) => observer.subscribe(notifyManager.batchCalls(onStoreChange)), diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index 281b5dd187..c59f4b341c 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -1,5 +1,4 @@ import * as React from 'react' -import { useSyncExternalStore } from './useSyncExternalStore' import type { QueryKey, QueryFunction } from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' @@ -176,7 +175,7 @@ export function useQueries({ const optimisticResult = observer.getOptimisticResult(defaultedQueries) - useSyncExternalStore( + React.useSyncExternalStore( React.useCallback( (onStoreChange) => isRestoring diff --git a/packages/react-query/src/useSyncExternalStore.native.ts b/packages/react-query/src/useSyncExternalStore.native.ts deleted file mode 100644 index f27367a18a..0000000000 --- a/packages/react-query/src/useSyncExternalStore.native.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Temporary workaround due to an issue with react-native uSES - https://github.com/TanStack/query/pull/3601 -// @ts-ignore -import { useSyncExternalStore } from 'use-sync-external-store/shim/index.native.js' - -export { useSyncExternalStore } diff --git a/packages/react-query/src/useSyncExternalStore.ts b/packages/react-query/src/useSyncExternalStore.ts deleted file mode 100644 index ed857e6740..0000000000 --- a/packages/react-query/src/useSyncExternalStore.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Temporary workaround due to an issue with react-native uSES - https://github.com/TanStack/query/pull/3601 -import { useSyncExternalStore as uSES } from 'use-sync-external-store/shim/index.js' - -export const useSyncExternalStore = uSES diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dfbabb119..e03748a32e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,6 @@ importers: '@rollup/plugin-replace': ^4.0.0 '@testing-library/jest-dom': ^5.16.4 '@testing-library/react': ^13.0.0 - '@testing-library/react-17': npm:@testing-library/react@12.1.4 '@testing-library/react-hooks': ^7.0.2 '@tsconfig/svelte': ^3.0.0 '@types/jest': ^26.0.4 @@ -56,9 +55,7 @@ importers: luxon: ^2.3.2 prettier: ^2.6.2 react: ^18.2.0 - react-17: npm:react@^17.0.2 react-dom: ^18.2.0 - react-dom-17: npm:react-dom@^17.0.2 rollup: ^2.70.2 rollup-plugin-size: ^0.2.2 rollup-plugin-svelte: ^7.1.0 @@ -85,7 +82,6 @@ importers: '@rollup/plugin-replace': 4.0.0_rollup@2.78.1 '@testing-library/jest-dom': 5.16.4 '@testing-library/react': 13.3.0_biqbaboplfbrettd7655fr4n2y - '@testing-library/react-17': /@testing-library/react/12.1.4_biqbaboplfbrettd7655fr4n2y '@testing-library/react-hooks': 7.0.2_biqbaboplfbrettd7655fr4n2y '@tsconfig/svelte': 3.0.0 '@types/jest': 26.0.24 @@ -125,9 +121,7 @@ importers: luxon: 2.4.0 prettier: 2.7.1 react: 18.2.0 - react-17: /react/17.0.2 react-dom: 18.2.0_react@18.2.0 - react-dom-17: /react-dom/17.0.2_react@18.2.0 rollup: 2.78.1 rollup-plugin-size: 0.2.2 rollup-plugin-svelte: 7.1.0_7rgskvp3hfjz55pue4td2a2xga @@ -740,27 +734,19 @@ importers: '@types/jscodeshift': ^0.11.3 '@types/react': ^18.0.14 '@types/react-dom': ^18.0.5 - '@types/use-sync-external-store': ^0.0.3 jscodeshift: ^0.13.1 react: ^18.2.0 - react-17: npm:react@^17.0.2 react-dom: ^18.2.0 - react-dom-17: npm:react-dom@^17.0.2 react-error-boundary: ^3.1.4 - use-sync-external-store: ^1.2.0 dependencies: '@tanstack/query-core': link:../query-core - use-sync-external-store: 1.2.0_react@18.2.0 devDependencies: '@types/jscodeshift': 0.11.5 '@types/react': 18.0.15 '@types/react-dom': 18.0.6 - '@types/use-sync-external-store': 0.0.3 jscodeshift: 0.13.1 react: 18.2.0 - react-17: /react/17.0.2 react-dom: 18.2.0_react@18.2.0 - react-dom-17: /react-dom/17.0.2_react@18.2.0 react-error-boundary: 3.1.4_react@18.2.0 packages/react-query-devtools: @@ -769,27 +755,19 @@ importers: '@tanstack/react-query': workspace:* '@types/react': ^18.0.14 '@types/react-dom': ^18.0.5 - '@types/use-sync-external-store': ^0.0.3 react: ^18.2.0 - react-17: npm:react@^17.0.2 react-dom: ^18.2.0 - react-dom-17: npm:react-dom@^17.0.2 react-error-boundary: ^3.1.4 superjson: ^1.10.0 - use-sync-external-store: ^1.2.0 dependencies: '@tanstack/match-sorter-utils': 8.7.0 superjson: 1.10.0 - use-sync-external-store: 1.2.0_react@18.2.0 devDependencies: '@tanstack/react-query': link:../react-query '@types/react': 18.0.15 '@types/react-dom': 18.0.6 - '@types/use-sync-external-store': 0.0.3 react: 18.2.0 - react-17: /react/17.0.2 react-dom: 18.2.0_react@18.2.0 - react-dom-17: /react-dom/17.0.2_react@18.2.0 react-error-boundary: 3.1.4_react@18.2.0 packages/react-query-persist-client: @@ -799,9 +777,7 @@ importers: '@types/react': ^18.0.14 '@types/react-dom': ^18.0.5 react: ^18.2.0 - react-17: npm:react@^17.0.2 react-dom: ^18.2.0 - react-dom-17: npm:react-dom@^17.0.2 dependencies: '@tanstack/query-persist-client-core': link:../query-persist-client-core devDependencies: @@ -809,9 +785,7 @@ importers: '@types/react': 18.0.15 '@types/react-dom': 18.0.6 react: 18.2.0 - react-17: /react/17.0.2 react-dom: 18.2.0_react@18.2.0 - react-dom-17: /react-dom/17.0.2_react@18.2.0 packages/solid-query: specifiers: @@ -5623,20 +5597,6 @@ packages: pretty-format: 27.5.1 dev: true - /@testing-library/dom/8.18.1: - resolution: {integrity: sha512-oEvsm2B/WtcHKE+IcEeeCqNU/ltFGaVyGbpcm4g/2ytuT49jrlH9x5qRKL/H3A6yfM4YAbSbC0ceT5+9CEXnLg==} - engines: {node: '>=12'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/runtime': 7.19.0 - '@types/aria-query': 4.2.2 - aria-query: 5.0.2 - chalk: 4.1.2 - dom-accessibility-api: 0.5.14 - lz-string: 1.4.4 - pretty-format: 27.5.1 - dev: true - /@testing-library/jest-dom/5.16.4: resolution: {integrity: sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==} engines: {node: '>=8', npm: '>=6', yarn: '>=1'} @@ -5674,20 +5634,6 @@ packages: react-error-boundary: 3.1.4_react@18.2.0 dev: true - /@testing-library/react/12.1.4_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==} - engines: {node: '>=12'} - peerDependencies: - react: '*' - react-dom: '*' - dependencies: - '@babel/runtime': 7.19.0 - '@testing-library/dom': 8.18.1 - '@types/react-dom': 18.0.6 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - dev: true - /@testing-library/react/13.3.0_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==} engines: {node: '>=12'} @@ -5916,10 +5862,6 @@ packages: '@types/jest': 26.0.24 dev: true - /@types/use-sync-external-store/0.0.3: - resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} - dev: true - /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -13573,17 +13515,6 @@ packages: scheduler: 0.20.2 dev: false - /react-dom/17.0.2_react@18.2.0: - resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} - peerDependencies: - react: 17.0.2 - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react: 18.2.0 - scheduler: 0.20.2 - dev: true - /react-dom/18.2.0_react@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -13609,7 +13540,7 @@ packages: peerDependencies: react: '>=16.13.1' dependencies: - '@babel/runtime': 7.18.9 + '@babel/runtime': 7.19.0 react: 18.2.0 dev: true @@ -13882,14 +13813,6 @@ packages: object-assign: 4.1.1 dev: false - /react/17.0.2: - resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - dev: true - /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -14302,6 +14225,7 @@ packages: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 + dev: false /scheduler/0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} @@ -15567,14 +15491,6 @@ packages: react: 18.2.0 dev: false - /use-sync-external-store/1.2.0_react@18.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false - /use/3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} diff --git a/rollup.config.ts b/rollup.config.ts index ee5ffe47fc..4ee2f5e597 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -92,20 +92,15 @@ export default function rollup(options: RollupOptions): RollupOptions[] { entryFile: [ 'src/index.ts', 'src/reactBatchedUpdates.native.ts', - 'src/useSyncExternalStore.native.ts', ], globals: { react: 'React', 'react-dom': 'ReactDOM', '@tanstack/query-core': 'QueryCore', - 'use-sync-external-store/shim/index.js': 'UseSyncExternalStore', - 'use-sync-external-store/shim/index.native.js': - 'UseSyncExternalStoreNative', 'react-native': 'ReactNative', }, bundleUMDGlobals: [ '@tanstack/query-core', - 'use-sync-external-store/shim/index.js', ], }), ...buildConfigs({ @@ -119,12 +114,10 @@ export default function rollup(options: RollupOptions): RollupOptions[] { 'react-dom': 'ReactDOM', '@tanstack/react-query': 'ReactQuery', '@tanstack/match-sorter-utils': 'MatchSorterUtils', - 'use-sync-external-store/shim/index.js': 'UseSyncExternalStore', superjson: 'SuperJson', }, bundleUMDGlobals: [ '@tanstack/match-sorter-utils', - 'use-sync-external-store/shim/index.js', 'superjson', ], }), @@ -139,7 +132,6 @@ export default function rollup(options: RollupOptions): RollupOptions[] { 'react-dom': 'ReactDOM', '@tanstack/react-query': 'ReactQuery', '@tanstack/match-sorter-utils': 'MatchSorterUtils', - 'use-sync-external-store/shim/index.js': 'UseSyncExternalStore', superjson: 'SuperJson', }, forceDevEnv: true, @@ -370,8 +362,6 @@ function cjs({ "require('./logger.js')": "require('./logger')", "require('./reactBatchedUpdates.js')": "require('./reactBatchedUpdates')", - "require('./useSyncExternalStore.js')": - "require('./useSyncExternalStore')", preventAssignment: true, delimiters: ['', ''], }), From 24b0ddd8ea5a2e50aaff28d0e530a141db6c5218 Mon Sep 17 00:00:00 2001 From: Roudain Sarhan <46911303+RodSarhan@users.noreply.github.com> Date: Wed, 11 Jan 2023 23:44:00 +0800 Subject: [PATCH 012/314] refactor(reactBatchedUpdates): remove the side effect that sets batchNotifyFn in react (#4699) * refactor(reactBatchedUpdates): remove the side effect that sets unstable_batchedUpdates as batchNotifyFn in react and react-native * docs(v5 migration guide): explain unstable_batchedUpdates removal Co-authored-by: Dominik Dorfmeister --- docs/react/guides/migrating-to-react-query-5.md | 15 +++++++++++++++ packages/react-query/package.json | 4 +--- packages/react-query/src/index.ts | 3 --- .../react-query/src/reactBatchedUpdates.native.ts | 4 ---- packages/react-query/src/reactBatchedUpdates.ts | 2 -- packages/react-query/src/setBatchUpdatesFn.ts | 4 ---- rollup.config.ts | 3 --- 7 files changed, 16 insertions(+), 19 deletions(-) delete mode 100644 packages/react-query/src/reactBatchedUpdates.native.ts delete mode 100644 packages/react-query/src/reactBatchedUpdates.ts delete mode 100644 packages/react-query/src/setBatchUpdatesFn.ts diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-react-query-5.md index a57341a479..c88c1c702e 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-react-query-5.md @@ -161,3 +161,18 @@ useQuery({ ### eslint `prefer-query-object-syntax` rule is removed Since the only supported syntax now is the object syntax, this rule is no longer needed + +### No longer using `unstable_batchedUpdates` as the batching function in React and React Native + +Since the function `unstable_batchedUpdates` is noop in React 18, it will no longer be automatically set as the batching function in `react-query`. + +If your framework supports a custom batching function, you can let TanStack Query know about it by calling `notifyManager.setBatchNotifyFunction`. + +For example, this is how the batch function is set in `solid-query`: + +```ts +import { notifyManager } from '@tanstack/query-core' +import { batch } from 'solid-js' + +notifyManager.setBatchNotifyFunction(batch) +``` diff --git a/packages/react-query/package.json b/packages/react-query/package.json index 4b572b33e7..ba9c3eef3f 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -21,9 +21,7 @@ }, "./package.json": "./package.json" }, - "sideEffects": [ - "./src/setBatchUpdatesFn.ts" - ], + "sideEffects": false, "scripts": { "clean": "rm -rf ./build", "test:codemods": "../../node_modules/.bin/jest --config codemods/jest.config.js", diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index b13a86a661..f906c6d847 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -1,8 +1,5 @@ /* istanbul ignore file */ -// Side effects -import './setBatchUpdatesFn' - // Re-export core export * from '@tanstack/query-core' diff --git a/packages/react-query/src/reactBatchedUpdates.native.ts b/packages/react-query/src/reactBatchedUpdates.native.ts deleted file mode 100644 index 7ca91f79f4..0000000000 --- a/packages/react-query/src/reactBatchedUpdates.native.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-ignore -// eslint-disable-next-line import/no-unresolved -import { unstable_batchedUpdates } from 'react-native' -export { unstable_batchedUpdates } diff --git a/packages/react-query/src/reactBatchedUpdates.ts b/packages/react-query/src/reactBatchedUpdates.ts deleted file mode 100644 index 1be1c6f314..0000000000 --- a/packages/react-query/src/reactBatchedUpdates.ts +++ /dev/null @@ -1,2 +0,0 @@ -import * as ReactDOM from 'react-dom' -export const unstable_batchedUpdates = ReactDOM.unstable_batchedUpdates diff --git a/packages/react-query/src/setBatchUpdatesFn.ts b/packages/react-query/src/setBatchUpdatesFn.ts deleted file mode 100644 index 7092e1a14c..0000000000 --- a/packages/react-query/src/setBatchUpdatesFn.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { notifyManager } from '@tanstack/query-core' -import { unstable_batchedUpdates } from './reactBatchedUpdates' - -notifyManager.setBatchNotifyFunction(unstable_batchedUpdates) diff --git a/rollup.config.ts b/rollup.config.ts index 4ee2f5e597..edd8cce6fb 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -91,7 +91,6 @@ export default function rollup(options: RollupOptions): RollupOptions[] { outputFile: 'index', entryFile: [ 'src/index.ts', - 'src/reactBatchedUpdates.native.ts', ], globals: { react: 'React', @@ -360,8 +359,6 @@ function cjs({ replace({ // TODO: figure out a better way to produce extensionless cjs imports "require('./logger.js')": "require('./logger')", - "require('./reactBatchedUpdates.js')": - "require('./reactBatchedUpdates')", preventAssignment: true, delimiters: ['', ''], }), From dadf96feb219aac55a2dd5f0a8ba6b0748a74843 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 11 Jan 2023 21:38:02 +0100 Subject: [PATCH 013/314] refactor(core): Use a Map internally for the QueryCache * refactor(core): Use a Map internally instead of an Object + an Array * feat(core): Allow users to provide a custom cache creator function * test(core): Add tests for custom query cache * refactor(core): Use public constructor parameter * refactor(core): experimental_createStore - rename from createCache - expose store interface - pass in the cache into the createStore function (this is a bit weird because cache.queries doesn't exist yet, but it does once the store is created and returned :/ - add another test that checks if the "size limit" use-case works - mark as experimental because the interface might not be final * oops * refactor(core): expose cache from query so that we can access query.queryCache to find and remove other queries * docs: docs for custom store * types: export the QueryStore type * feat(custom-cache): revert exposing custom cache most things can be done by doing queryCache.subscribe, and we can still add this later if necessary * feat(custom-cache): cleanup forgotton export * stabilize a test --- docs/config.json | 8 +-- ...to-react-query-5.md => migrating-to-v5.md} | 2 +- packages/query-core/src/query.ts | 3 -- packages/query-core/src/queryCache.ts | 51 ++++++++---------- .../query-core/src/tests/queryCache.test.tsx | 52 ++++++++++++++++--- .../src/__tests__/useQuery.test.tsx | 19 ++++--- 6 files changed, 84 insertions(+), 51 deletions(-) rename docs/react/guides/{migrating-to-react-query-5.md => migrating-to-v5.md} (99%) diff --git a/docs/config.json b/docs/config.json index e335efc20f..af38944468 100644 --- a/docs/config.json +++ b/docs/config.json @@ -177,16 +177,16 @@ "to": "react/guides/does-this-replace-client-state" }, { - "label": "Migrating to React Query 3", + "label": "Migrating to v3", "to": "react/guides/migrating-to-react-query-3" }, { - "label": "Migrating to React Query 4", + "label": "Migrating to v4", "to": "react/guides/migrating-to-react-query-4" }, { - "label": "Migrating to React Query 5", - "to": "react/guides/migrating-to-react-query-5" + "label": "Migrating to v5", + "to": "react/guides/migrating-v5" } ] }, diff --git a/docs/react/guides/migrating-to-react-query-5.md b/docs/react/guides/migrating-to-v5.md similarity index 99% rename from docs/react/guides/migrating-to-react-query-5.md rename to docs/react/guides/migrating-to-v5.md index c88c1c702e..5dd1a30fec 100644 --- a/docs/react/guides/migrating-to-react-query-5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -1,5 +1,5 @@ --- -id: migrating-to-react-query-5 +id: migrating-to-v5 title: Migrating to TanStack Query v5 --- diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 4931dea7f1..0c3fbef51d 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -159,7 +159,6 @@ export class Query< private observers: QueryObserver[] private defaultOptions?: QueryOptions private abortSignalConsumed: boolean - constructor(config: QueryConfig) { super() @@ -174,11 +173,9 @@ export class Query< this.initialState = config.state || getDefaultState(this.options) this.state = this.initialState } - get meta(): QueryMeta | undefined { return this.options.meta } - private setOptions( options?: QueryOptions, ): void { diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index ff9720e909..b124a8ba0f 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -15,10 +15,6 @@ interface QueryCacheConfig { onSuccess?: (data: unknown, query: Query) => void } -interface QueryHashMap { - [hash: string]: Query -} - interface NotifyEventQueryAdded { type: 'added' query: Query @@ -72,16 +68,10 @@ type QueryCacheListener = (event: QueryCacheNotifyEvent) => void // CLASS export class QueryCache extends Subscribable { - config: QueryCacheConfig - - private queries: Query[] - private queriesMap: QueryHashMap + private queries = new Map() - constructor(config?: QueryCacheConfig) { + constructor(public config: QueryCacheConfig = {}) { super() - this.config = config || {} - this.queries = [] - this.queriesMap = {} } build( @@ -111,9 +101,9 @@ export class QueryCache extends Subscribable { } add(query: Query): void { - if (!this.queriesMap[query.queryHash]) { - this.queriesMap[query.queryHash] = query - this.queries.push(query) + if (!this.queries.has(query.queryHash)) { + this.queries.set(query.queryHash, query) + this.notify({ type: 'added', query, @@ -122,15 +112,13 @@ export class QueryCache extends Subscribable { } remove(query: Query): void { - const queryInMap = this.queriesMap[query.queryHash] + const queryInMap = this.queries.get(query.queryHash) if (queryInMap) { query.destroy() - this.queries = this.queries.filter((x) => x !== query) - if (queryInMap === query) { - delete this.queriesMap[query.queryHash] + this.queries.delete(query.queryHash) } this.notify({ type: 'removed', query }) @@ -139,7 +127,7 @@ export class QueryCache extends Subscribable { clear(): void { notifyManager.batch(() => { - this.queries.forEach((query) => { + this.getAll().forEach((query) => { this.remove(query) }) }) @@ -149,15 +137,17 @@ export class QueryCache extends Subscribable { TQueryFnData = unknown, TError = Error, TData = TQueryFnData, - TQueyKey extends QueryKey = QueryKey, + TQueryKey extends QueryKey = QueryKey, >( queryHash: string, - ): Query | undefined { - return this.queriesMap[queryHash] + ): Query | undefined { + return this.queries.get(queryHash) as + | Query + | undefined } getAll(): Query[] { - return this.queries + return [...this.queries.values()] } find( @@ -167,13 +157,16 @@ export class QueryCache extends Subscribable { filters.exact = true } - return this.queries.find((query) => matchQuery(filters, query)) + return this.getAll().find((query) => matchQuery(filters, query)) as + | Query + | undefined } findAll(filters: QueryFilters = {}): Query[] { + const queries = this.getAll() return Object.keys(filters).length > 0 - ? this.queries.filter((query) => matchQuery(filters, query)) - : this.queries + ? queries.filter((query) => matchQuery(filters, query)) + : queries } notify(event: QueryCacheNotifyEvent) { @@ -186,7 +179,7 @@ export class QueryCache extends Subscribable { onFocus(): void { notifyManager.batch(() => { - this.queries.forEach((query) => { + this.getAll().forEach((query) => { query.onFocus() }) }) @@ -194,7 +187,7 @@ export class QueryCache extends Subscribable { onOnline(): void { notifyManager.batch(() => { - this.queries.forEach((query) => { + this.getAll().forEach((query) => { query.onOnline() }) }) diff --git a/packages/query-core/src/tests/queryCache.test.tsx b/packages/query-core/src/tests/queryCache.test.tsx index c7ae5bfdab..686ea06c6c 100644 --- a/packages/query-core/src/tests/queryCache.test.tsx +++ b/packages/query-core/src/tests/queryCache.test.tsx @@ -1,5 +1,5 @@ import { sleep, queryKey, createQueryClient } from './utils' -import type { QueryClient } from '..' +import { QueryClient } from '..' import { QueryCache, QueryObserver } from '..' import type { Query } from '.././query' import { waitFor } from '@testing-library/react' @@ -94,6 +94,46 @@ describe('queryCache', () => { await sleep(100) expect(callback).toHaveBeenCalled() }) + + test('should be able to limit cache size', async () => { + const testCache = new QueryCache() + + const unsubscribe = testCache.subscribe((event) => { + if (event.type === 'added') { + if (testCache.getAll().length > 2) { + testCache + .findAll({ + type: 'inactive', + predicate: (q) => q !== event.query, + }) + .forEach((query) => { + testCache.remove(query) + }) + } + } + }) + + const testClient = new QueryClient({ queryCache: testCache }) + + await testClient.prefetchQuery({ + queryKey: ['key1'], + queryFn: () => 'data1', + }) + expect(testCache.findAll().length).toBe(1) + await testClient.prefetchQuery({ + queryKey: ['key2'], + queryFn: () => 'data2', + }) + expect(testCache.findAll().length).toBe(2) + await testClient.prefetchQuery({ + queryKey: ['key3'], + queryFn: () => 'data3', + }) + expect(testCache.findAll().length).toBe(1) + expect(testCache.findAll()[0]!.state.data).toBe('data3') + + unsubscribe() + }) }) describe('find', () => { @@ -286,15 +326,15 @@ describe('queryCache', () => { // Directly add the query from the cache // to simulate a race condition - const query = queryCache['queriesMap'][hash] as Query + const query = queryCache['queries'].get(hash) as Query const queryClone = Object.assign({}, query) // No error should be thrown when trying to add the query queryCache.add(queryClone) - expect(queryCache['queries'].length).toEqual(1) + expect(queryCache.getAll().length).toEqual(1) // Clean-up to avoid an error when queryClient.clear() - delete queryCache['queriesMap'][hash] + queryCache['queries'].delete(hash) }) describe('QueryCache.remove', () => { @@ -309,9 +349,9 @@ describe('queryCache', () => { // Directly remove the query from the cache // to simulate a race condition - const query = queryCache['queriesMap'][hash] as Query + const query = queryCache['queries'].get(hash) as Query const queryClone = Object.assign({}, query) - delete queryCache['queriesMap'][hash] + queryCache['queries'].delete(hash) // No error should be thrown when trying to remove the query expect(() => queryCache.remove(queryClone)).not.toThrow() diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index c1241d2db8..41cac6a68e 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -673,26 +673,29 @@ describe('useQuery', () => { it('should call onSettled after a query has been fetched with an error', async () => { const key = queryKey() - const states: UseQueryResult[] = [] const onSettled = jest.fn() + const error = new Error('error') function Page() { const state = useQuery({ queryKey: key, - queryFn: () => Promise.reject('error'), + queryFn: async () => { + await sleep(10) + return Promise.reject(error) + }, retry: false, onSettled, }) - states.push(state) - return null + return
    status: {state.status}
    } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await sleep(10) - expect(states.length).toBe(2) + await waitFor(() => { + rendered.getByText('status: error') + }) expect(onSettled).toHaveBeenCalledTimes(1) - expect(onSettled).toHaveBeenCalledWith(undefined, 'error') + expect(onSettled).toHaveBeenCalledWith(undefined, error) }) it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => { From 324e69c5fc39ee32b2577a8a78f72bb59317991f Mon Sep 17 00:00:00 2001 From: ilyasmez Date: Fri, 13 Jan 2023 11:59:28 +0100 Subject: [PATCH 014/314] fix(focusManager): stop listening for `focus` events (#4805) The `focus` event had many caveats (as discussed in #4797), so we now listen only for `visibilitychange` event. --- docs/react/guides/important-defaults.md | 2 -- docs/react/guides/window-focus-refetching.md | 27 ++------------- docs/react/reference/focusManager.md | 6 ++-- packages/query-core/src/focusManager.ts | 4 +-- .../src/tests/focusManager.test.tsx | 34 +++++++------------ packages/query-core/src/tests/query.test.tsx | 3 +- .../src/__tests__/useQuery.test.tsx | 22 ++++++------ .../src/__tests__/createQuery.test.tsx | 20 +++++------ 8 files changed, 41 insertions(+), 77 deletions(-) diff --git a/docs/react/guides/important-defaults.md b/docs/react/guides/important-defaults.md index f4416a599c..57d867c60e 100644 --- a/docs/react/guides/important-defaults.md +++ b/docs/react/guides/important-defaults.md @@ -15,8 +15,6 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul - The network is reconnected - The query is optionally configured with a refetch interval -If you see a refetch that you are not expecting, it is likely because you just focused the window and TanStack Query is doing a [`refetchOnWindowFocus`](../guides/window-focus-refetching). During development, this will probably be triggered more frequently, especially because focusing between the Browser DevTools and your app will also cause a fetch, so be aware of that. - > To change this functionality, you can use options like `refetchOnMount`, `refetchOnWindowFocus`, `refetchOnReconnect` and `refetchInterval`. - Query results that have no more active instances of `useQuery`, `useInfiniteQuery` or query observers are labeled as "inactive" and remain in the cache in case they are used again at a later time. diff --git a/docs/react/guides/window-focus-refetching.md b/docs/react/guides/window-focus-refetching.md index 66e0b4a9a2..0a03ec2d18 100644 --- a/docs/react/guides/window-focus-refetching.md +++ b/docs/react/guides/window-focus-refetching.md @@ -48,36 +48,19 @@ In rare circumstances, you may want to manage your own window focus events that ```tsx focusManager.setEventListener((handleFocus) => { - // Listen to visibilitychange and focus + // Listen to visibilitychange if (typeof window !== 'undefined' && window.addEventListener) { window.addEventListener('visibilitychange', handleFocus, false) - window.addEventListener('focus', handleFocus, false) } return () => { // Be sure to unsubscribe if a new handler is set window.removeEventListener('visibilitychange', handleFocus) - window.removeEventListener('focus', handleFocus) } }) ``` [//]: # 'Example3' - -## Ignoring Iframe Focus Events - -A great use-case for replacing the focus handler is that of iframe events. Iframes present problems with detecting window focus by both double-firing events and also firing false-positive events when focusing or using iframes within your app. If you experience this, you should use an event handler that ignores these events as much as possible. I recommend [this one](https://gist.github.com/tannerlinsley/1d3a2122332107fcd8c9cc379be10d88)! It can be set up in the following way: - -[//]: # 'Example4' - -```tsx -import { focusManager } from '@tanstack/react-query' -import onWindowFocus from './onWindowFocus' // The gist above - -focusManager.setEventListener(onWindowFocus) // Boom! -``` - -[//]: # 'Example4' [//]: # 'ReactNative' ## Managing Focus in React Native @@ -105,7 +88,7 @@ useEffect(() => { ## Managing focus state -[//]: # 'Example5' +[//]: # 'Example4' ```tsx import { focusManager } from '@tanstack/react-query' @@ -117,8 +100,4 @@ focusManager.setFocused(true) focusManager.setFocused(undefined) ``` -[//]: # 'Example5' - -## Pitfalls & Caveats - -Some browser internal dialogue windows, such as spawned by `alert()` or file upload dialogues (as created by ``) might also trigger focus refetching after they close. This can result in unwanted side effects, as the refetching might trigger component unmounts or remounts before your file upload handler is executed. See [this issue on GitHub](https://github.com/tannerlinsley/react-query/issues/2960) for background and possible workarounds. +[//]: # 'Example4' diff --git a/docs/react/reference/focusManager.md b/docs/react/reference/focusManager.md index de9482abd2..edf2c94e78 100644 --- a/docs/react/reference/focusManager.md +++ b/docs/react/reference/focusManager.md @@ -20,17 +20,15 @@ Its available methods are: ```tsx import { focusManager } from '@tanstack/react-query' -focusManager.setEventListener(handleFocus => { - // Listen to visibilitychange and focus +focusManager.setEventListener((handleFocus) => { + // Listen to visibilitychange if (typeof window !== 'undefined' && window.addEventListener) { window.addEventListener('visibilitychange', handleFocus, false) - window.addEventListener('focus', handleFocus, false) } return () => { // Be sure to unsubscribe if a new handler is set window.removeEventListener('visibilitychange', handleFocus) - window.removeEventListener('focus', handleFocus) } }) ``` diff --git a/packages/query-core/src/focusManager.ts b/packages/query-core/src/focusManager.ts index 5397c6a6c6..3ec79dacf7 100644 --- a/packages/query-core/src/focusManager.ts +++ b/packages/query-core/src/focusManager.ts @@ -18,14 +18,12 @@ export class FocusManager extends Subscribable { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!isServer && window.addEventListener) { const listener = () => onFocus() - // Listen to visibillitychange and focus + // Listen to visibilitychange window.addEventListener('visibilitychange', listener, false) - window.addEventListener('focus', listener, false) return () => { // Be sure to unsubscribe if a new handler is set window.removeEventListener('visibilitychange', listener) - window.removeEventListener('focus', listener) } } return diff --git a/packages/query-core/src/tests/focusManager.test.tsx b/packages/query-core/src/tests/focusManager.test.tsx index 645a93f477..2250aa23ee 100644 --- a/packages/query-core/src/tests/focusManager.test.tsx +++ b/packages/query-core/src/tests/focusManager.test.tsx @@ -87,31 +87,23 @@ describe('focusManager', () => { }) it('should replace default window listener when a new event listener is set', async () => { - const addEventListenerSpy = jest.spyOn( - globalThis.window, - 'addEventListener', - ) + const unsubscribeSpy = jest.fn().mockImplementation(() => undefined) + const handlerSpy = jest.fn().mockImplementation(() => unsubscribeSpy) - const removeEventListenerSpy = jest.spyOn( - globalThis.window, - 'removeEventListener', - ) + focusManager.setEventListener(() => handlerSpy()) - // Should set the default event listener with window event listeners const unsubscribe = focusManager.subscribe(() => undefined) - expect(addEventListenerSpy).toHaveBeenCalledTimes(2) - - // Should replace the window default event listener by a new one - // and it should call window.removeEventListener twice - focusManager.setEventListener(() => { - return () => void 0 - }) - expect(removeEventListenerSpy).toHaveBeenCalledTimes(2) + // Should call the custom event once + expect(handlerSpy).toHaveBeenCalledTimes(1) unsubscribe() - addEventListenerSpy.mockRestore() - removeEventListenerSpy.mockRestore() + + // Should unsubscribe our event event + expect(unsubscribeSpy).toHaveBeenCalledTimes(1) + + handlerSpy.mockRestore() + unsubscribeSpy.mockRestore() }) test('should call removeEventListener when last listener unsubscribes', () => { @@ -127,12 +119,12 @@ describe('focusManager', () => { const unsubscribe1 = focusManager.subscribe(() => undefined) const unsubscribe2 = focusManager.subscribe(() => undefined) - expect(addEventListenerSpy).toHaveBeenCalledTimes(2) // visibilitychange + focus + expect(addEventListenerSpy).toHaveBeenCalledTimes(1) // visibilitychange event unsubscribe1() expect(removeEventListenerSpy).toHaveBeenCalledTimes(0) unsubscribe2() - expect(removeEventListenerSpy).toHaveBeenCalledTimes(2) // visibilitychange + focus + expect(removeEventListenerSpy).toHaveBeenCalledTimes(1) // visibilitychange event }) test('should keep setup function even if last listener unsubscribes', () => { diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 64c87be01a..8fb7298185 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -86,7 +86,7 @@ describe('query', () => { // Reset visibilityState to original value visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) // There should not be a result yet expect(result).toBeUndefined() @@ -181,7 +181,6 @@ describe('query', () => { } finally { // Reset visibilityState to original value visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) } }) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 41cac6a68e..96961399bd 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -2608,7 +2608,7 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('default')) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) expect(queryFn).not.toHaveBeenCalled() @@ -2635,7 +2635,7 @@ describe('useQuery', () => { await sleep(10) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await sleep(10) @@ -2666,7 +2666,7 @@ describe('useQuery', () => { await sleep(10) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await sleep(10) @@ -2697,7 +2697,7 @@ describe('useQuery', () => { await sleep(10) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await sleep(10) @@ -2732,7 +2732,7 @@ describe('useQuery', () => { await sleep(20) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await sleep(20) @@ -2774,7 +2774,7 @@ describe('useQuery', () => { expect(states[1]).toMatchObject({ data: 0, isFetching: false }) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await rendered.findByText('data: 1') @@ -2786,7 +2786,7 @@ describe('useQuery', () => { expect(states[3]).toMatchObject({ data: 1, isFetching: false }) act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await sleep(20) @@ -3519,7 +3519,7 @@ describe('useQuery', () => { act(() => { // reset visibilityState to original value visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) // Wait for the final result @@ -3601,7 +3601,7 @@ describe('useQuery', () => { act(() => { // reset visibilityState to original value visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) await waitFor(() => expect(states.length).toBe(4)) @@ -5327,7 +5327,7 @@ describe('useQuery', () => { rendered.getByText('status: success, fetchStatus: paused'), ) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(15) await waitFor(() => @@ -5493,7 +5493,7 @@ describe('useQuery', () => { // triggers a second pause act(() => { - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) }) onlineMock.mockReturnValue(true) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 5e708a97d9..424c7a1516 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -2474,7 +2474,7 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('default')) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) expect(queryFn).not.toHaveBeenCalled() }) @@ -2505,7 +2505,7 @@ describe('createQuery', () => { await sleep(10) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(10) @@ -2540,7 +2540,7 @@ describe('createQuery', () => { await sleep(10) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(10) @@ -2575,7 +2575,7 @@ describe('createQuery', () => { await sleep(10) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(10) @@ -2613,7 +2613,7 @@ describe('createQuery', () => { await sleep(20) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(20) @@ -2658,7 +2658,7 @@ describe('createQuery', () => { expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) expect(states[1]).toMatchObject({ data: 0, isFetching: false }) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await screen.findByText('data: 1') @@ -3469,7 +3469,7 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('failureReason fetching error 1')) visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) // Wait for the final result await waitFor(() => screen.getByText('failureCount 4')) @@ -3564,7 +3564,7 @@ describe('createQuery', () => { // reset visibilityState to original value visibilityMock.mockRestore() - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await waitFor(() => expect(states.length).toBe(4)) @@ -5367,7 +5367,7 @@ describe('createQuery', () => { screen.getByText('status: success, fetchStatus: paused'), ) - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) await sleep(15) await waitFor(() => @@ -5544,7 +5544,7 @@ describe('createQuery', () => { ) // triggers a second pause - window.dispatchEvent(new FocusEvent('focus')) + window.dispatchEvent(new Event('visibilitychange')) onlineMock.mockReturnValue(true) window.dispatchEvent(new Event('online')) From 096da932ec5c8865125c65d39f631084c94555f4 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 13 Jan 2023 13:09:26 +0100 Subject: [PATCH 015/314] docs: migration guide for window focus changes --- docs/react/guides/migrating-to-v5.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 5dd1a30fec..3e07451f04 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -162,6 +162,10 @@ useQuery({ Since the only supported syntax now is the object syntax, this rule is no longer needed +### Window focus refetching no longer listens to the `focus` event + +The `visibilitychange` event is used exclusively now. This is possible because we only support browsers that support the `visibilitychange` event. This fixes a bunch of issues [as listed here](https://github.com/TanStack/query/pull/4805). + ### No longer using `unstable_batchedUpdates` as the batching function in React and React Native Since the function `unstable_batchedUpdates` is noop in React 18, it will no longer be automatically set as the batching function in `react-query`. @@ -171,8 +175,8 @@ If your framework supports a custom batching function, you can let TanStack Quer For example, this is how the batch function is set in `solid-query`: ```ts -import { notifyManager } from '@tanstack/query-core' -import { batch } from 'solid-js' - -notifyManager.setBatchNotifyFunction(batch) +import { notifyManager } from '@tanstack/query-core' +import { batch } from 'solid-js' + +notifyManager.setBatchNotifyFunction(batch) ``` From c01c3ff9dd497c3bddaf5e5ee97e0453a6cc7f89 Mon Sep 17 00:00:00 2001 From: Damian Osipiuk Date: Fri, 13 Jan 2023 14:34:27 +0100 Subject: [PATCH 016/314] feat: remove `keepPreviousData` in favor of `placeholderData` (#4715) * feat: remove `keepPreviousData` in favor of `placeholderData` BREAKING CHANGE: removed `keepPreviousData` in favor of `placeholderData` identity function * fix: change generic name * docs: extend diff in migration guide * tests: remove irrelevant tests * fix: useQueries placeholder data parameter removed * feat: keepPreviousData identity function * fix: formatting * fix: ts 4.7 error * fix: formatting * docs: add a note about useQueries * chore: fix formatting * fix: formatting * Apply suggestions from code review Co-authored-by: Dominik Dorfmeister --- docs/react/guides/migrating-to-v5.md | 54 +++- docs/react/guides/paginated-queries.md | 20 +- docs/react/reference/QueryClient.md | 2 +- docs/react/reference/useQuery.md | 13 +- examples/react/pagination/pages/index.js | 11 +- packages/query-core/src/index.ts | 8 +- packages/query-core/src/queriesObserver.ts | 17 +- packages/query-core/src/queryObserver.ts | 26 +- packages/query-core/src/types.ts | 20 +- packages/query-core/src/utils.ts | 6 + .../src/__tests__/useInfiniteQuery.test.tsx | 23 +- .../src/__tests__/useQueries.test.tsx | 245 --------------- .../src/__tests__/useQuery.test.tsx | 90 +++--- packages/react-query/src/useQueries.ts | 13 +- .../__tests__/createInfiniteQuery.test.tsx | 25 +- .../src/__tests__/createQueries.test.tsx | 279 ------------------ .../src/__tests__/createQuery.test.tsx | 64 ++-- packages/solid-query/src/createQueries.ts | 13 +- packages/vue-query/src/useQueries.ts | 46 ++- 19 files changed, 253 insertions(+), 722 deletions(-) diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 3e07451f04..8d674534bb 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -1,5 +1,5 @@ --- -id: migrating-to-v5 +id: migrating-to-react-query-5 title: Migrating to TanStack Query v5 --- @@ -150,11 +150,11 @@ If you want to throw something that isn't an Error, you'll now have to set the g useQuery({ queryKey: ['some-query'], queryFn: async () => { - if (Math.random() > 0.5) { - throw 'some error' - } - return 42 - } + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + }, }) ``` @@ -162,6 +162,48 @@ useQuery({ Since the only supported syntax now is the object syntax, this rule is no longer needed +### Removed `keepPreviousData` in favor of `placeholderData` identity function + +We have removed the `keepPreviousData` option and `isPreviousData` flag as they were doing mostly the same thing as `placeholderData` and `isPlaceholderData` flag. + +To achieve the same functionality as `keepPreviousData`, we have added previous query `data` as an argument to `placeholderData` function. +Therefore you just need to provide an identity function to `placeholderData` or use `keepPreviousData` function returned from Tanstack Query. + +> A note here is that `useQueries` would not receive `previousData` in the `placeholderData` function as argument. This is due to a dynamic nature of queries passed in the array, which may lead to a different shape of result from placeholder and queryFn. + +```diff +const { + data, +- isPreviousData, ++ isPlaceholderData, +} = useQuery({ + queryKey, + queryFn, +- keepPreviousData: true, ++ placeholderData: keepPreviousData +}); +``` + +There are some caveats to this change however, which you must be aware of: + +- `placeholderData` will always put you into `success` state, while `keepPreviousData` gave you the status of the previous query. That status could be `error` if we have data fetched successfully and then got a background refetch error. However, the error itself was not shared, so we decided to stick with behavior of `placeholderData`. +- `keepPreviousData` gave you the `dataUpdatedAt` timestamp of the previous data, while with `placeholderData`, `dataUpdatedAt` will stay at `0`. This might be annoying if you want to show that timestamp continuously on screen. However you might get around it with `useEffect`. + + ```ts + const [updatedAt, setUpdatedAt] = useState(0) + + const { data, dataUpdatedAt } = useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + }) + + useEffect(() => { + if (dataUpdatedAt > updatedAt) { + setUpdatedAt(dataUpdatedAt) + } + }, [dataUpdatedAt]) + ``` + ### Window focus refetching no longer listens to the `focus` event The `visibilitychange` event is used exclusively now. This is possible because we only support browsers that support the `visibilitychange` event. This fixes a bunch of issues [as listed here](https://github.com/TanStack/query/pull/4805). diff --git a/docs/react/guides/paginated-queries.md b/docs/react/guides/paginated-queries.md index b1bbe85f4d..bee69f3e4e 100644 --- a/docs/react/guides/paginated-queries.md +++ b/docs/react/guides/paginated-queries.md @@ -18,15 +18,15 @@ However, if you run this simple example, you might notice something strange: **The UI jumps in and out of the `success` and `loading` states because each new page is treated like a brand new query.** -This experience is not optimal and unfortunately is how many tools today insist on working. But not TanStack Query! As you may have guessed, TanStack Query comes with an awesome feature called `keepPreviousData` that allows us to get around this. +This experience is not optimal and unfortunately is how many tools today insist on working. But not TanStack Query! As you may have guessed, TanStack Query comes with an awesome feature called `placeholderData` that allows us to get around this. -## Better Paginated Queries with `keepPreviousData` +## Better Paginated Queries with `placeholderData` -Consider the following example where we would ideally want to increment a pageIndex (or cursor) for a query. If we were to use `useQuery`, **it would still technically work fine**, but the UI would jump in and out of the `success` and `loading` states as different queries are created and destroyed for each page or cursor. By setting `keepPreviousData` to `true` we get a few new things: +Consider the following example where we would ideally want to increment a pageIndex (or cursor) for a query. If we were to use `useQuery`, **it would still technically work fine**, but the UI would jump in and out of the `success` and `loading` states as different queries are created and destroyed for each page or cursor. By setting `placeholderData` to `(previousData) => previousData` or `keepPreviousData` function exported from TanStack Query, we get a few new things: - **The data from the last successful fetch available while new data is being requested, even though the query key has changed**. - When the new data arrives, the previous `data` is seamlessly swapped to show the new data. -- `isPreviousData` is made available to know what data the query is currently providing you +- `isPlaceholderData` is made available to know what data the query is currently providing you [//]: # 'Example2' ```tsx @@ -41,11 +41,11 @@ function Todos() { error, data, isFetching, - isPreviousData, + isPlaceholderData, } = useQuery({ queryKey: ['projects', page], queryFn: () => fetchProjects(page), - keepPreviousData : true + placeholderData: keepPreviousData, }) return ( @@ -70,12 +70,12 @@ function Todos() { {' '} @@ -86,6 +86,6 @@ function Todos() { ``` [//]: # 'Example2' -## Lagging Infinite Query results with `keepPreviousData` +## Lagging Infinite Query results with `placeholderData` -While not as common, the `keepPreviousData` option also works flawlessly with the `useInfiniteQuery` hook, so you can seamlessly allow your users to continue to see cached data while infinite query keys change over time. +While not as common, the `placeholderData` option also works flawlessly with the `useInfiniteQuery` hook, so you can seamlessly allow your users to continue to see cached data while infinite query keys change over time. diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index de01824703..0453e71056 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -95,7 +95,7 @@ try { **Options** -The options for `fetchQuery` are exactly the same as those of [`useQuery`](../reference/useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, throwErrors, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. +The options for `fetchQuery` are exactly the same as those of [`useQuery`](../reference/useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, throwErrors, select, suspense, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. **Returns** diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index 9358686904..175d102705 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -19,7 +19,6 @@ const { isLoading, isLoadingError, isPlaceholderData, - isPreviousData, isRefetchError, isRefetching, isStale, @@ -35,7 +34,6 @@ const { networkMode, initialData, initialDataUpdatedAt, - keepPreviousData, meta, notifyOnChangeProps, onError, @@ -160,15 +158,12 @@ const { - `initialDataUpdatedAt: number | (() => number | undefined)` - Optional - If set, this value will be used as the time (in milliseconds) of when the `initialData` itself was last updated. -- `placeholderData: TData | () => TData` +- `placeholderData: TData | (previousValue: TData) => TData` - Optional - If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. - `placeholderData` is **not persisted** to the cache -- `keepPreviousData: boolean` - - Optional - - Defaults to `false` - - If set, any previous `data` will be kept when fetching new data because the query key changed. - `structuralSharing: boolean | ((oldData: TData | undefined, newData: TData) => TData)` + - If you provide a function for `placeholderData`, as a first argument you will receive previously watched query data if available +- `structuralSharing: boolean | ((oldData: TData | undefined, newData: TData) => TData)` - Optional - Defaults to `true` - If set to `false`, structural sharing between query results will be disabled. @@ -215,8 +210,6 @@ const { - Will be `true` if the data in the cache is invalidated or if the data is older than the given `staleTime`. - `isPlaceholderData: boolean` - Will be `true` if the data shown is the placeholder data. -- `isPreviousData: boolean` - - Will be `true` when `keepPreviousData` is set and data from the previous query is returned. - `isFetched: boolean` - Will be `true` if the query has been fetched. - `isFetchedAfterMount: boolean` diff --git a/examples/react/pagination/pages/index.js b/examples/react/pagination/pages/index.js index 8e63d685bb..41693acfff 100644 --- a/examples/react/pagination/pages/index.js +++ b/examples/react/pagination/pages/index.js @@ -5,6 +5,7 @@ import { useQueryClient, QueryClient, QueryClientProvider, + keepPreviousData, } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' @@ -27,22 +28,22 @@ function Example() { const queryClient = useQueryClient() const [page, setPage] = React.useState(0) - const { status, data, error, isFetching, isPreviousData } = useQuery({ + const { status, data, error, isFetching, isPlaceholderData } = useQuery({ queryKey: ['projects', page], queryFn: () => fetchProjects(page), - keepPreviousData: true, + placeholderData: keepPreviousData, staleTime: 5000, }) // Prefetch the next page! React.useEffect(() => { - if (!isPreviousData && data?.hasMore) { + if (!isPlaceholderData && data?.hasMore) { queryClient.prefetchQuery({ queryKey: ['projects', page + 1], queryFn: () => fetchProjects(page + 1), }) } - }, [data, isPreviousData, page, queryClient]) + }, [data, isPlaceholderData, page, queryClient]) return (
    @@ -78,7 +79,7 @@ function Example() { onClick={() => { setPage((old) => (data?.hasMore ? old + 1 : old)) }} - disabled={isPreviousData || !data?.hasMore} + disabled={isPlaceholderData || !data?.hasMore} > Next Page diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index d2bee42dd8..3c705d286a 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -11,7 +11,13 @@ export { MutationObserver } from './mutationObserver' export { notifyManager } from './notifyManager' export { focusManager } from './focusManager' export { onlineManager } from './onlineManager' -export { hashQueryKey, replaceEqualDeep, isError, isServer } from './utils' +export { + hashQueryKey, + replaceEqualDeep, + isError, + isServer, + keepPreviousData, +} from './utils' export type { MutationFilters, QueryFilters, Updater } from './utils' export { isCancelledError } from './retryer' export { dehydrate, hydrate } from './hydration' diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index c9fbc5e164..b3a7dbddae 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -155,11 +155,6 @@ export class QueriesObserver extends Subscribable { !matchedQueryHashes.includes(defaultedOptions.queryHash), ) - const unmatchedObservers = prevObservers.filter( - (prevObserver) => - !matchingObservers.some((match) => match.observer === prevObserver), - ) - const getObserver = (options: QueryObserverOptions): QueryObserver => { const defaultedOptions = this.client.defaultQueryOptions(options) const currentObserver = this.observersMap[defaultedOptions.queryHash!] @@ -167,17 +162,7 @@ export class QueriesObserver extends Subscribable { } const newOrReusedObservers: QueryObserverMatch[] = unmatchedQueries.map( - (options, index) => { - if (options.keepPreviousData) { - // return previous data from one of the observers that no longer match - const previouslyUsedObserver = unmatchedObservers[index] - if (previouslyUsedObserver !== undefined) { - return { - defaultedQueryOptions: options, - observer: previouslyUsedObserver, - } - } - } + (options) => { return { defaultedQueryOptions: options, observer: getObserver(options), diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 0dcb1bbb2c..303e88ed08 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -422,8 +422,7 @@ export class QueryObserver< : this.previousQueryResult const { state } = query - let { dataUpdatedAt, error, errorUpdatedAt, fetchStatus, status } = state - let isPreviousData = false + let { error, errorUpdatedAt, fetchStatus, status } = state let isPlaceholderData = false let data: TData | undefined @@ -440,7 +439,7 @@ export class QueryObserver< fetchStatus = canFetch(query.options.networkMode) ? 'fetching' : 'paused' - if (!dataUpdatedAt) { + if (!state.dataUpdatedAt) { status = 'loading' } } @@ -449,20 +448,8 @@ export class QueryObserver< } } - // Keep previous data if needed - if ( - options.keepPreviousData && - !state.dataUpdatedAt && - prevQueryResult?.isSuccess && - status !== 'error' - ) { - data = prevQueryResult.data - dataUpdatedAt = prevQueryResult.dataUpdatedAt - status = prevQueryResult.status - isPreviousData = true - } // Select data if needed - else if (options.select && typeof state.data !== 'undefined') { + if (options.select && typeof state.data !== 'undefined') { // Memoize select result if ( prevResult && @@ -507,7 +494,9 @@ export class QueryObserver< } else { placeholderData = typeof options.placeholderData === 'function' - ? (options.placeholderData as PlaceholderDataFunction)() + ? ( + options.placeholderData as unknown as PlaceholderDataFunction + )(prevQueryResult?.data as TQueryData | undefined) : options.placeholderData if (options.select && typeof placeholderData !== 'undefined') { try { @@ -548,7 +537,7 @@ export class QueryObserver< isError, isInitialLoading: isLoading && isFetching, data, - dataUpdatedAt, + dataUpdatedAt: state.dataUpdatedAt, error, errorUpdatedAt, failureCount: state.fetchFailureCount, @@ -563,7 +552,6 @@ export class QueryObserver< isLoadingError: isError && state.dataUpdatedAt === 0, isPaused: fetchStatus === 'paused', isPlaceholderData, - isPreviousData, isRefetchError: isError && state.dataUpdatedAt !== 0, isStale: isStale(query, options), refetch: this.refetch, diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 0ff4b0f347..8312473fd4 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -27,7 +27,15 @@ export interface QueryFunctionContext< export type InitialDataFunction = () => T | undefined -export type PlaceholderDataFunction = () => TResult | undefined +type NonFunctionGuard = T extends Function ? never : T + +export type PlaceholderDataFunction = ( + previousData: TQueryData | undefined, +) => TQueryData | undefined + +export type QueriesPlaceholderDataFunction = () => + | TQueryData + | undefined export type QueryKeyHashFunction = ( queryKey: TQueryKey, @@ -231,15 +239,12 @@ export interface QueryObserverOptions< * Defaults to `false`. */ suspense?: boolean - /** - * Set this to `true` to keep the previous `data` when fetching based on a new query key. - * Defaults to `false`. - */ - keepPreviousData?: boolean /** * If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. */ - placeholderData?: TQueryData | PlaceholderDataFunction + placeholderData?: + | NonFunctionGuard + | PlaceholderDataFunction> _optimisticResults?: 'optimistic' | 'isRestoring' } @@ -379,7 +384,6 @@ export interface QueryObserverBaseResult { isInitialLoading: boolean isPaused: boolean isPlaceholderData: boolean - isPreviousData: boolean isRefetchError: boolean isRefetching: boolean isStale: boolean diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 5adf603245..0f16c10b20 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -357,3 +357,9 @@ export function replaceData< } return data } + +export function keepPreviousData( + previousData: T | undefined, +): T | undefined { + return previousData +} diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx index 821bc0729f..2c994dd5db 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx @@ -13,7 +13,7 @@ import type { QueryFunctionContext, UseInfiniteQueryResult, } from '..' -import { QueryCache, useInfiniteQuery } from '..' +import { QueryCache, useInfiniteQuery, keepPreviousData } from '..' interface Result { items: number[] @@ -85,7 +85,6 @@ describe('useInfiniteQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -118,7 +117,6 @@ describe('useInfiniteQuery', () => { isInitialLoading: false, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -168,7 +166,7 @@ describe('useInfiniteQuery', () => { await waitFor(() => expect(noThrow).toBe(true)) }) - it('should keep the previous data when keepPreviousData is set', async () => { + it('should keep the previous data when placeholderData is set', async () => { const key = queryKey() const states: UseInfiniteQueryResult[] = [] @@ -181,9 +179,8 @@ describe('useInfiniteQuery', () => { await sleep(10) return `${pageParam}-${order}` }, - getNextPageParam: () => 1, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', }) @@ -216,28 +213,28 @@ describe('useInfiniteQuery', () => { isFetching: true, isFetchingNextPage: false, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[1]).toMatchObject({ data: { pages: ['0-desc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[2]).toMatchObject({ data: { pages: ['0-desc'] }, isFetching: true, isFetchingNextPage: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[3]).toMatchObject({ data: { pages: ['0-desc', '1-desc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[4]).toMatchObject({ @@ -245,7 +242,7 @@ describe('useInfiniteQuery', () => { isFetching: true, isFetchingNextPage: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Hook state update expect(states[5]).toMatchObject({ @@ -253,14 +250,14 @@ describe('useInfiniteQuery', () => { isFetching: true, isFetchingNextPage: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) expect(states[6]).toMatchObject({ data: { pages: ['0-asc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 2dfd3373ec..f10f22decb 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -73,251 +73,6 @@ describe('useQueries', () => { expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) }) - it('should keep previous data if amount of queries is the same', async () => { - const key1 = queryKey() - const key2 = queryKey() - const states: UseQueryResult[][] = [] - - function Page() { - const [count, setCount] = React.useState(1) - const result = useQueries({ - queries: [ - { - queryKey: [key1, count], - keepPreviousData: true, - queryFn: async () => { - await sleep(10) - return count * 2 - }, - }, - { - queryKey: [key2, count], - keepPreviousData: true, - queryFn: async () => { - await sleep(35) - return count * 5 - }, - }, - ], - }) - states.push(result) - - const isFetching = result.some((r) => r.isFetching) - - return ( -
    -
    - data1: {String(result[0].data ?? 'null')}, data2:{' '} - {String(result[1].data ?? 'null')} -
    -
    isFetching: {String(isFetching)}
    - -
    - ) - } - - const rendered = renderWithClient(queryClient, ) - - await waitFor(() => rendered.getByText('data1: 2, data2: 5')) - fireEvent.click(rendered.getByRole('button', { name: /inc/i })) - - await waitFor(() => rendered.getByText('data1: 4, data2: 10')) - await waitFor(() => rendered.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 4, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should keep previous data for variable amounts of useQueries', async () => { - const key = queryKey() - const states: UseQueryResult[][] = [] - - function Page() { - const [count, setCount] = React.useState(2) - const result = useQueries({ - queries: Array.from({ length: count }, (_, i) => ({ - queryKey: [key, count, i + 1], - keepPreviousData: true, - queryFn: async () => { - await sleep(35 * (i + 1)) - return (i + 1) * count * 2 - }, - })), - }) - - states.push(result) - - const isFetching = result.some((r) => r.isFetching) - - return ( -
    -
    data: {result.map((it) => it.data).join(',')}
    -
    isFetching: {String(isFetching)}
    - -
    - ) - } - - const rendered = renderWithClient(queryClient, ) - - await waitFor(() => rendered.getByText('data: 4,8')) - fireEvent.click(rendered.getByRole('button', { name: /inc/i })) - - await waitFor(() => rendered.getByText('data: 6,12,18')) - await waitFor(() => rendered.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 6, isPreviousData: false, isFetching: false }, - { status: 'success', data: 12, isPreviousData: false, isFetching: false }, - { status: 'success', data: 18, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should keep previous data when switching between queries', async () => { - const key = queryKey() - const states: UseQueryResult[][] = [] - - function Page() { - const [series1, setSeries1] = React.useState(1) - const [series2, setSeries2] = React.useState(2) - const ids = [series1, series2] - - const result = useQueries({ - queries: ids.map((id) => { - return { - queryKey: [key, id], - queryFn: async () => { - await sleep(5) - return id * 5 - }, - keepPreviousData: true, - } - }), - }) - - states.push(result) - - const isFetching = result.some((r) => r.isFetching) - - return ( -
    -
    - data1: {String(result[0]?.data ?? 'null')}, data2:{' '} - {String(result[1]?.data ?? 'null')} -
    -
    isFetching: {String(isFetching)}
    - - -
    - ) - } - - const rendered = renderWithClient(queryClient, ) - - await waitFor(() => rendered.getByText('data1: 5, data2: 10')) - fireEvent.click(rendered.getByRole('button', { name: /setSeries2/i })) - - await waitFor(() => rendered.getByText('data1: 5, data2: 15')) - fireEvent.click(rendered.getByRole('button', { name: /setSeries1/i })) - - await waitFor(() => rendered.getByText('data1: 10, data2: 15')) - await waitFor(() => rendered.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - { status: 'success', data: 15, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should not go to infinite render loop with previous data when toggling queries', async () => { - const key = queryKey() - const states: UseQueryResult[][] = [] - - function Page() { - const [enableId1, setEnableId1] = React.useState(true) - const ids = enableId1 ? [1, 2] : [2] - - const result = useQueries({ - queries: ids.map((id) => { - return { - queryKey: [key, id], - queryFn: async () => { - await sleep(10) - return id * 5 - }, - keepPreviousData: true, - } - }), - }) - - states.push(result) - - const isFetching = result.some((r) => r.isFetching) - - return ( -
    -
    - data1: {String(result[0]?.data ?? 'null')}, data2:{' '} - {String(result[1]?.data ?? 'null')} -
    -
    isFetching: {String(isFetching)}
    - - -
    - ) - } - - const rendered = renderWithClient(queryClient, ) - - await waitFor(() => rendered.getByText('data1: 5, data2: 10')) - fireEvent.click(rendered.getByRole('button', { name: /set1Disabled/i })) - - await waitFor(() => rendered.getByText('data1: 10, data2: null')) - await waitFor(() => rendered.getByText('isFetching: false')) - fireEvent.click(rendered.getByRole('button', { name: /set2Enabled/i })) - - await waitFor(() => rendered.getByText('data1: 5, data2: 10')) - await waitFor(() => rendered.getByText('isFetching: false')) - - await waitFor(() => expect(states.length).toBe(6)) - - expect(states[0]).toMatchObject([ - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - ]) - expect(states[1]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[2]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[3]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: true }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[4]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: true }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[5]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - }) - it('handles type parameter - tuple of tuples', async () => { const key1 = queryKey() const key2 = queryKey() diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 96961399bd..96ac6a1de9 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -20,7 +20,7 @@ import type { UseQueryOptions, UseQueryResult, } from '..' -import { QueryCache, useQuery } from '..' +import { QueryCache, useQuery, keepPreviousData } from '..' import { ErrorBoundary } from 'react-error-boundary' describe('useQuery', () => { @@ -265,7 +265,6 @@ describe('useQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -292,7 +291,6 @@ describe('useQuery', () => { isInitialLoading: false, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -349,7 +347,6 @@ describe('useQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -376,7 +373,6 @@ describe('useQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -403,7 +399,6 @@ describe('useQuery', () => { isInitialLoading: false, isLoadingError: true, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -1664,7 +1659,7 @@ describe('useQuery', () => { }) }) - it('should keep the previous data when keepPreviousData is set', async () => { + it('should keep the previous data when placeholderData is set', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -1677,7 +1672,7 @@ describe('useQuery', () => { await sleep(10) return count }, - keepPreviousData: true, + placeholderData: keepPreviousData, }) states.push(state) @@ -1703,32 +1698,32 @@ describe('useQuery', () => { data: undefined, isFetching: true, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched expect(states[1]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[2]).toMatchObject({ data: 0, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // New data expect(states[3]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should transition to error state when keepPreviousData is set', async () => { + it('should transition to error state when placeholderData is set', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -1742,9 +1737,8 @@ describe('useQuery', () => { } return Promise.resolve(count) }, - retry: false, - keepPreviousData: true, + placeholderData: keepPreviousData, }) states.push(state) @@ -1753,7 +1747,7 @@ describe('useQuery', () => {

    data: {state.data}

    error: {state.error?.message}

    -

    previous data: {state.isPreviousData}

    +

    placeholder data: {state.isPlaceholderData}

    ) } @@ -1772,7 +1766,7 @@ describe('useQuery', () => { isFetching: true, status: 'loading', error: null, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched expect(states[1]).toMatchObject({ @@ -1780,7 +1774,7 @@ describe('useQuery', () => { isFetching: false, status: 'success', error: null, - isPreviousData: false, + isPlaceholderData: false, }) // rerender Page 1 expect(states[2]).toMatchObject({ @@ -1788,7 +1782,7 @@ describe('useQuery', () => { isFetching: true, status: 'success', error: null, - isPreviousData: true, + isPlaceholderData: true, }) // Hook state update expect(states[3]).toMatchObject({ @@ -1796,7 +1790,7 @@ describe('useQuery', () => { isFetching: true, status: 'success', error: null, - isPreviousData: true, + isPlaceholderData: true, }) // New data expect(states[4]).toMatchObject({ @@ -1804,7 +1798,7 @@ describe('useQuery', () => { isFetching: false, status: 'success', error: null, - isPreviousData: false, + isPlaceholderData: false, }) // rerender Page 2 expect(states[5]).toMatchObject({ @@ -1812,7 +1806,7 @@ describe('useQuery', () => { isFetching: true, status: 'success', error: null, - isPreviousData: true, + isPlaceholderData: true, }) // Hook state update again expect(states[6]).toMatchObject({ @@ -1820,19 +1814,19 @@ describe('useQuery', () => { isFetching: true, status: 'success', error: null, - isPreviousData: true, + isPlaceholderData: true, }) // Error expect(states[7]).toMatchObject({ data: undefined, isFetching: false, status: 'error', - isPreviousData: false, + isPlaceholderData: false, }) expect(states[7]?.error).toHaveProperty('message', 'Error test') }) - it('should not show initial data from next query if keepPreviousData is set', async () => { + it('should not show initial data from next query if placeholderData is set', async () => { const key = queryKey() const states: DefinedUseQueryResult[] = [] @@ -1846,7 +1840,7 @@ describe('useQuery', () => { return count }, initialData: 99, - keepPreviousData: true, + placeholderData: keepPreviousData, }) states.push(state) @@ -1881,39 +1875,39 @@ describe('useQuery', () => { data: 99, isFetching: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched expect(states[1]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[2]).toMatchObject({ data: 99, isFetching: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Hook state update expect(states[3]).toMatchObject({ data: 99, isFetching: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // New data expect(states[4]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should keep the previous data on disabled query when keepPreviousData is set', async () => { + it('should keep the previous data on disabled query when placeholderData is set', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -1927,7 +1921,7 @@ describe('useQuery', () => { return count }, enabled: false, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', }) @@ -1976,46 +1970,46 @@ describe('useQuery', () => { data: undefined, isFetching: false, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetching query expect(states[1]).toMatchObject({ data: undefined, isFetching: true, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched query expect(states[2]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[3]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Fetching new query expect(states[4]).toMatchObject({ data: 0, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Fetched new query expect(states[5]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should keep the previous data on disabled query when keepPreviousData is set and switching query key multiple times', async () => { + it('should keep the previous data on disabled query when placeholderData is set and switching query key multiple times', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -2033,7 +2027,7 @@ describe('useQuery', () => { return count }, enabled: false, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', }) @@ -2067,35 +2061,35 @@ describe('useQuery', () => { data: 10, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[1]).toMatchObject({ data: 10, isFetching: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // State update expect(states[2]).toMatchObject({ data: 10, isFetching: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Refetch expect(states[3]).toMatchObject({ data: 10, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Refetch done expect(states[4]).toMatchObject({ data: 12, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) @@ -3864,7 +3858,7 @@ describe('useQuery', () => { expect(results[1]).toMatchObject({ data: 1, isFetching: false }) }) - it('should show the correct data when switching keys with initialData, keepPreviousData & staleTime', async () => { + it('should show the correct data when switching keys with initialData, placeholderData & staleTime', async () => { const key = queryKey() const ALL_TODOS = [ @@ -3886,7 +3880,7 @@ describe('useQuery', () => { initialData() { return filter === '' ? initialTodos : undefined }, - keepPreviousData: true, + placeholderData: keepPreviousData, staleTime: 5000, }) diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index c59f4b341c..6e39248a02 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -1,6 +1,10 @@ import * as React from 'react' -import type { QueryKey, QueryFunction } from '@tanstack/query-core' +import type { + QueryKey, + QueryFunction, + QueriesPlaceholderDataFunction, +} from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { UseQueryOptions, UseQueryResult } from './types' @@ -25,7 +29,12 @@ type UseQueryOptionsForUseQueries< TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = Omit, 'context'> +> = Omit< + UseQueryOptions, + 'context' | 'placeholderData' +> & { + placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction +} // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx index 1546c92db8..ee88ae121d 100644 --- a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -16,7 +16,12 @@ import type { InfiniteData, QueryFunctionContext, } from '..' -import { createInfiniteQuery, QueryCache, QueryClientProvider } from '..' +import { + createInfiniteQuery, + QueryCache, + QueryClientProvider, + keepPreviousData, +} from '..' import { Blink, queryKey, setActTimeout } from './utils' interface Result { @@ -95,7 +100,6 @@ describe('useInfiniteQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -128,7 +132,6 @@ describe('useInfiniteQuery', () => { isInitialLoading: false, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -182,7 +185,7 @@ describe('useInfiniteQuery', () => { await waitFor(() => expect(noThrow).toBe(true)) }) - it('should keep the previous data when keepPreviousData is set', async () => { + it('should keep the previous data when placeholderData is set', async () => { const key = queryKey() const states: CreateInfiniteQueryResult[] = [] @@ -197,7 +200,7 @@ describe('useInfiniteQuery', () => { }, getNextPageParam: () => 1, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', })) @@ -236,28 +239,28 @@ describe('useInfiniteQuery', () => { isFetching: true, isFetchingNextPage: false, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[1]).toMatchObject({ data: { pages: ['0-desc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[2]).toMatchObject({ data: { pages: ['0-desc'] }, isFetching: true, isFetchingNextPage: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) expect(states[3]).toMatchObject({ data: { pages: ['0-desc', '1-desc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[4]).toMatchObject({ @@ -265,14 +268,14 @@ describe('useInfiniteQuery', () => { isFetching: true, isFetchingNextPage: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) expect(states[5]).toMatchObject({ data: { pages: ['0-asc'] }, isFetching: false, isFetchingNextPage: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) diff --git a/packages/solid-query/src/__tests__/createQueries.test.tsx b/packages/solid-query/src/__tests__/createQueries.test.tsx index a451068a6f..490ca37534 100644 --- a/packages/solid-query/src/__tests__/createQueries.test.tsx +++ b/packages/solid-query/src/__tests__/createQueries.test.tsx @@ -5,7 +5,6 @@ import * as QueriesObserverModule from '../../../query-core/src/queriesObserver' import type { QueryFunctionContext, QueryKey } from '@tanstack/query-core' import { createContext, - createMemo, createRenderEffect, createSignal, ErrorBoundary, @@ -88,284 +87,6 @@ describe('useQueries', () => { expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) }) - it('should keep previous data if amount of queries is the same', async () => { - const key1 = queryKey() - const key2 = queryKey() - const states: CreateQueryResult[][] = [] - - function Page() { - const [count, setCount] = createSignal(1) - const result = createQueries(() => ({ - queries: [ - { - queryKey: [key1, count()], - keepPreviousData: true, - queryFn: async () => { - await sleep(10) - return count() * 2 - }, - }, - { - queryKey: [key2, count()], - keepPreviousData: true, - queryFn: async () => { - await sleep(35) - return count() * 5 - }, - }, - ], - })) - - createRenderEffect(() => { - states.push([...result]) - }) - - const isFetching = createMemo(() => result.some((r) => r.isFetching)) - - return ( -
    -
    - data1: {String(result[0].data ?? 'null')}, data2:{' '} - {String(result[1].data ?? 'null')} -
    -
    isFetching: {String(isFetching())}
    - -
    - ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data1: 2, data2: 5')) - fireEvent.click(screen.getByRole('button', { name: /inc/i })) - - await waitFor(() => screen.getByText('data1: 4, data2: 10')) - await waitFor(() => screen.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 4, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should keep previous data for variable amounts of useQueries', async () => { - const key = queryKey() - const states: CreateQueryResult[][] = [] - - function Page() { - const [count, setCount] = createSignal(2) - const result = createQueries(() => ({ - queries: Array.from({ length: count() }, (_, i) => ({ - queryKey: [key, count(), i + 1], - keepPreviousData: true, - queryFn: async () => { - await sleep(35 * (i + 1)) - return (i + 1) * count() * 2 - }, - })), - })) - - createRenderEffect(() => { - states.push([...result]) - }) - - const isFetching = createMemo(() => result.some((r) => r.isFetching)) - - return ( -
    -
    data: {result.map((it) => it.data).join(',')}
    -
    isFetching: {String(isFetching())}
    - -
    - ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data: 4,8')) - fireEvent.click(screen.getByRole('button', { name: /inc/i })) - - await waitFor(() => screen.getByText('data: 6,12,18')) - await waitFor(() => screen.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 6, isPreviousData: false, isFetching: false }, - { status: 'success', data: 12, isPreviousData: false, isFetching: false }, - { status: 'success', data: 18, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should keep previous data when switching between queries', async () => { - const key = queryKey() - const states: CreateQueryResult[][] = [] - - function Page() { - const [series1, setSeries1] = createSignal(1) - const [series2, setSeries2] = createSignal(2) - const ids = [series1, series2] - - const result = createQueries(() => ({ - queries: ids.map((id) => { - return { - queryKey: [key, id()], - queryFn: async () => { - await sleep(5) - return id() * 5 - }, - keepPreviousData: true, - } - }), - })) - - createRenderEffect(() => { - states.push([...result]) - }) - - const isFetching = createMemo(() => result.some((r) => r.isFetching)) - - return ( -
    -
    - data1: {String(result[0]?.data ?? 'null')}, data2:{' '} - {String(result[1]?.data ?? 'null')} -
    -
    isFetching: {String(isFetching())}
    - - -
    - ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data1: 5, data2: 10')) - fireEvent.click(screen.getByRole('button', { name: /setSeries2/i })) - - await waitFor(() => screen.getByText('data1: 5, data2: 15')) - fireEvent.click(screen.getByRole('button', { name: /setSeries1/i })) - - await waitFor(() => screen.getByText('data1: 10, data2: 15')) - await waitFor(() => screen.getByText('isFetching: false')) - - expect(states[states.length - 1]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - { status: 'success', data: 15, isPreviousData: false, isFetching: false }, - ]) - }) - - it('should not go to infinite render loop with previous data when toggling queries', async () => { - const key = queryKey() - const states: CreateQueryResult[][] = [] - - function Page() { - const [enableId1, setEnableId1] = createSignal(true) - const ids = createMemo(() => (enableId1() ? [1, 2] : [2])) - - const result = createQueries(() => ({ - queries: ids().map((id) => { - return { - queryKey: [key, id], - queryFn: async () => { - await sleep(5) - return id * 5 - }, - keepPreviousData: true, - } - }), - })) - - createRenderEffect(() => { - states.push([...result]) - }) - - const text = createMemo(() => { - return result - .map((r, idx) => `data${idx + 1}: ${r.data ?? 'null'}`) - .join(' ') - }) - - const isFetching = createMemo(() => result.some((r) => r.isFetching)) - - return ( -
    -
    {text()}
    -
    isFetching: {String(isFetching())}
    - - -
    - ) - } - - render(() => ( - - - - )) - - await waitFor(() => screen.getByText('data1: 5 data2: 10')) - fireEvent.click(screen.getByRole('button', { name: /set1Disabled/i })) - - await waitFor(() => screen.getByText('data1: 10')) - await waitFor(() => screen.getByText('isFetching: false')) - fireEvent.click(screen.getByRole('button', { name: /set2Enabled/i })) - - await waitFor(() => screen.getByText('data1: 5 data2: 10')) - await waitFor(() => screen.getByText('isFetching: false')) - - await waitFor(() => expect(states.length).toBe(6)) - - expect(states[0]).toMatchObject([ - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - ]) - expect(states[1]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - ]) - expect(states[2]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[3]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[4]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: true }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[5]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - }) - it('handles type parameter - tuple of tuples', async () => { const key1 = queryKey() const key2 = queryKey() diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 424c7a1516..49eba723bf 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -17,7 +17,12 @@ import type { DefinedCreateQueryResult, QueryFunction, } from '..' -import { createQuery, QueryCache, QueryClientProvider } from '..' +import { + createQuery, + QueryCache, + QueryClientProvider, + keepPreviousData, +} from '..' import { Blink, createQueryClient, @@ -289,7 +294,6 @@ describe('createQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -316,7 +320,6 @@ describe('createQuery', () => { isInitialLoading: false, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -378,7 +381,6 @@ describe('createQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -405,7 +407,6 @@ describe('createQuery', () => { isInitialLoading: true, isLoadingError: false, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -432,7 +433,6 @@ describe('createQuery', () => { isInitialLoading: false, isLoadingError: true, isPlaceholderData: false, - isPreviousData: false, isRefetchError: false, isRefetching: false, isStale: true, @@ -1597,7 +1597,7 @@ describe('createQuery', () => { }) }) - it('should keep the previous data when keepPreviousData is set', async () => { + it('should keep the previous data when placeholderData is set', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -1610,7 +1610,7 @@ describe('createQuery', () => { await sleep(10) return count() }, - keepPreviousData: true, + placeholderData: keepPreviousData, })) createRenderEffect(() => { @@ -1639,32 +1639,32 @@ describe('createQuery', () => { data: undefined, isFetching: true, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched expect(states[1]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[2]).toMatchObject({ data: 0, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // New data expect(states[3]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should not show initial data from next query if keepPreviousData is set', async () => { + it('should not show initial data from next query if placeholderData is set', async () => { const key = queryKey() const states: DefinedCreateQueryResult[] = [] @@ -1678,7 +1678,7 @@ describe('createQuery', () => { return count() }, initialData: 99, - keepPreviousData: true, + placeholderData: keepPreviousData, })) createRenderEffect(() => { @@ -1719,32 +1719,32 @@ describe('createQuery', () => { data: 99, isFetching: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched expect(states[1]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[2]).toMatchObject({ data: 99, isFetching: true, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // New data expect(states[3]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should keep the previous data on disabled query when keepPreviousData is set', async () => { + it('should keep the previous data on disabled query when placeholderData is set to identity function', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -1758,7 +1758,7 @@ describe('createQuery', () => { return count() }, enabled: false, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', })) @@ -1797,46 +1797,46 @@ describe('createQuery', () => { data: undefined, isFetching: false, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetching query expect(states[1]).toMatchObject({ data: undefined, isFetching: true, isSuccess: false, - isPreviousData: false, + isPlaceholderData: false, }) // Fetched query expect(states[2]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[3]).toMatchObject({ data: 0, isFetching: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Fetching new query expect(states[4]).toMatchObject({ data: 0, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Fetched new query expect(states[5]).toMatchObject({ data: 1, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) - it('should keep the previous data on disabled query when keepPreviousData is set and switching query key multiple times', async () => { + it('should keep the previous data on disabled query when placeholderData is set and switching query key multiple times', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -1854,7 +1854,7 @@ describe('createQuery', () => { return count() }, enabled: false, - keepPreviousData: true, + placeholderData: keepPreviousData, notifyOnChangeProps: 'all', })) @@ -1893,28 +1893,28 @@ describe('createQuery', () => { data: 10, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) // Set state expect(states[1]).toMatchObject({ data: 10, isFetching: false, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Refetch expect(states[2]).toMatchObject({ data: 10, isFetching: true, isSuccess: true, - isPreviousData: true, + isPlaceholderData: true, }) // Refetch done expect(states[3]).toMatchObject({ data: 12, isFetching: false, isSuccess: true, - isPreviousData: false, + isPlaceholderData: false, }) }) diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index d245bdcc1a..bb54964039 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -1,4 +1,8 @@ -import type { QueryFunction, QueryKey } from '@tanstack/query-core' +import type { + QueriesPlaceholderDataFunction, + QueryFunction, + QueryKey, +} from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { createComputed, onCleanup, onMount } from 'solid-js' import { createStore, unwrap } from 'solid-js/store' @@ -12,7 +16,12 @@ type CreateQueryOptionsForCreateQueries< TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = Omit, 'context'> +> = Omit< + SolidQueryOptions, + 'context' | 'placeholderData' +> & { + placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction +} // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index ab40ebcb09..de86b08be9 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -1,5 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { QueriesObserver } from '@tanstack/query-core' +import type { + QueriesPlaceholderDataFunction, + QueryKey, +} from '@tanstack/query-core' import { computed, onScopeDispose, @@ -17,6 +21,20 @@ import { cloneDeepUnref } from './utils' import type { UseQueryOptions } from './useQuery' import type { QueryClient } from './queryClient' +// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. +// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +type UseQueryOptionsForUseQueries< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = Omit< + UseQueryOptions, + 'context' | 'placeholderData' +> & { + placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction +} + // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 @@ -27,28 +45,28 @@ type GetOptions = error?: infer TError data: infer TData } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { queryFnData: infer TQueryFnData; error?: infer TError } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { data: infer TData; error?: infer TError } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData] T extends [infer TQueryFnData, infer TError, infer TData] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends [infer TQueryFnData, infer TError] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends [infer TQueryFnData] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided T extends { queryFn?: QueryFunction select: (data: any) => infer TData } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { queryFn?: QueryFunction } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : // Fallback - UseQueryOptions + UseQueryOptionsForUseQueries type GetResults = // Part 1: responsible for mapping explicit type parameter to function result, if object @@ -84,7 +102,7 @@ export type UseQueriesOptions< Result extends any[] = [], Depth extends ReadonlyArray = [], > = Depth['length'] extends MAXIMUM_DEPTH - ? UseQueryOptions[] + ? UseQueryOptionsForUseQueries[] : T extends [] ? [] : T extends [infer Head] @@ -95,15 +113,15 @@ export type UseQueriesOptions< ? T : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type! // use this to infer the param types in the case of Array.map() argument - T extends UseQueryOptions< + T extends UseQueryOptionsForUseQueries< infer TQueryFnData, infer TError, infer TData, infer TQueryKey >[] - ? UseQueryOptions[] + ? UseQueryOptionsForUseQueries[] : // Fallback - UseQueryOptions[] + UseQueryOptionsForUseQueries[] /** * UseQueriesResults reducer recursively maps type param to results @@ -120,7 +138,7 @@ export type UseQueriesResults< ? [...Result, GetResults] : T extends [infer Head, ...infer Tail] ? UseQueriesResults<[...Tail], [...Result, GetResults], [...Depth, 1]> - : T extends UseQueryOptions< + : T extends UseQueryOptionsForUseQueries< infer TQueryFnData, infer TError, infer TData, From b3f3c59e0f5a90337af0ca3933f87b5a375fceb7 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 13 Jan 2023 22:17:32 +0100 Subject: [PATCH 017/314] refactor: size improvements * refactor: updated supported browsers to get smaller bundlesize * refactor: switch to private fields and methods * refactor: remove getAbortController function because with our new browserslist, we'll always have an AbortController * refactor: make updateResult private * this didn't work returning the object and inlining the call yields "cannot redefine signal" ... * refactor: remove/move/inline some code * tests: focusManager fix focusManager tests by not accessing private methods * tests: mutationCache remove a test that tests an impossible case * refactor: mutationObserver as the test said, there is no way to trigger #notify without listeners, so we can remove the whole test + code that relates to it * refactor: mutation.setState as the tests says, it's unused, so we can remove it completely * test(mutations): rewrite test to not rely on internals we can add the same observer twice and check that the observerAdded event has only been fired once * tests: onlineManager fix onlineManager tests by not accessing private methods * tests: remove test for a case that cannot happen * refactor(core): make initialState and revertState private they have not been accessed outside of query * tests: rewrite query tests to not depend on internals * tests: rewrite queryCache tests to not depend on internals * refactor: notifyOptions.cache it was always set to true - there is no way to _not_ notify the cache * chore: remove unused imports * refactor: unify partialMatchKey and partialDeepEqual * refactor: remove Array check when checking for keys during filtering a simple truthy check suffices, as we can only pass an Array or undefined in here * refactor: rename hashQueryKey to hashKey because we also use it for mutationKeys * tests: remove unnecessary condition signal is now always defined * tests: get rid of annoying false-positive console.error log * tests: don't rely on internals re-wrote some tests and removed some others that weren't easily testable without relying on internals * docs: document size improvements * tests: fix copyButton test * test(vue-query): fix tests accessing private props * resolve merge conflict Co-authored-by: Damian Osipiuk --- .browserslistrc | 12 +- babel.config.js | 1 + docs/react/guides/migrating-to-v5.md | 8 + docs/react/installation.md | 12 +- packages/query-core/src/focusManager.ts | 28 +- packages/query-core/src/index.ts | 8 +- .../query-core/src/infiniteQueryBehavior.ts | 8 +- packages/query-core/src/mutation.ts | 81 +++--- packages/query-core/src/mutationCache.ts | 31 +- packages/query-core/src/mutationObserver.ts | 117 ++++---- packages/query-core/src/onlineManager.ts | 28 +- packages/query-core/src/queriesObserver.ts | 87 +++--- packages/query-core/src/query.ts | 176 ++++++------ packages/query-core/src/queryCache.ts | 14 +- packages/query-core/src/queryClient.ts | 128 ++++----- packages/query-core/src/queryObserver.ts | 269 +++++++++--------- packages/query-core/src/removable.ts | 10 +- .../src/tests/focusManager.test.tsx | 16 +- .../src/tests/mutationCache.test.tsx | 40 --- .../src/tests/mutationObserver.test.tsx | 30 -- .../query-core/src/tests/mutations.test.tsx | 51 +--- .../src/tests/onlineManager.test.tsx | 16 +- .../src/tests/queriesObserver.test.tsx | 38 +-- packages/query-core/src/tests/query.test.tsx | 152 ++-------- .../query-core/src/tests/queryCache.test.tsx | 31 +- .../query-core/src/tests/queryClient.test.tsx | 8 +- .../src/tests/queryObserver.test.tsx | 64 +---- packages/query-core/src/tests/utils.test.tsx | 42 +-- packages/query-core/src/types.ts | 2 +- packages/query-core/src/utils.ts | 51 +--- .../src/__tests__/Explorer.test.tsx | 5 + .../src/__tests__/suspense.test.tsx | 5 - .../src/__tests__/suspense.test.tsx | 5 - .../vue-query/src/__tests__/utils.test.ts | 8 +- .../src/__tests__/vueQueryPlugin.test.ts | 18 +- packages/vue-query/src/utils.ts | 9 - 36 files changed, 634 insertions(+), 975 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index 01b924286a..980cd0a027 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,7 +1,7 @@ # Browsers we support -Chrome >= 73 -Firefox >= 78 -Edge >= 79 -Safari >= 12.0 -iOS >= 12.0 -opera >= 53 +Chrome >= 84 +Firefox >= 90 +Edge >= 84 +Safari >= 15 +iOS >= 15 +opera >= 70 diff --git a/babel.config.js b/babel.config.js index dc23100fd4..05a15ce4c0 100644 --- a/babel.config.js +++ b/babel.config.js @@ -9,6 +9,7 @@ module.exports = { '@babel/preset-env', { loose, + bugfixes: true, modules: false, exclude: [ '@babel/plugin-transform-regenerator', diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 8d674534bb..d246ecde12 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -136,6 +136,14 @@ You can achieve the same functionality by passing a function to `structuralShari + structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) ``` +### Supported Browsers + +We have updated our browserslist to produce a more modern, performant and smaller bundle. You can read about the requirements [here](../installation#requirements). + +### Private class fields and methods + +TanStack Query has always had private fields and methods on classes, but they weren't really private - they were just private in `TypeScript`. We now use [ECMAScript Private class features](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields), which means those fields are now truly private and can't be accessed from the outside at runtime. + ### The `useErrorBoundary` prop has been renamed to `throwErrors` To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. diff --git a/docs/react/installation.md b/docs/react/installation.md index b59a58e246..6d212e0c7e 100644 --- a/docs/react/installation.md +++ b/docs/react/installation.md @@ -38,12 +38,12 @@ Once you've added this you will have access to the `window.ReactQuery` object an React Query is optimized for modern browsers. It is compatible with the following browsers config ``` -Chrome >= 73 -Firefox >= 78 -Edge >= 79 -Safari >= 12.0 -iOS >= 12.0 -opera >= 53 +Chrome >= 84 +Firefox >= 90 +Edge >= 84 +Safari >= 15 +iOS >= 15 +opera >= 70 ``` > Depending on your environment, you might need to add polyfills. If you want to support older browsers, you need to transpile the library from `node_modules` yourselves. diff --git a/packages/query-core/src/focusManager.ts b/packages/query-core/src/focusManager.ts index 3ec79dacf7..033a712262 100644 --- a/packages/query-core/src/focusManager.ts +++ b/packages/query-core/src/focusManager.ts @@ -6,14 +6,14 @@ type SetupFn = ( ) => (() => void) | undefined export class FocusManager extends Subscribable { - private focused?: boolean - private cleanup?: () => void + #focused?: boolean + #cleanup?: () => void - private setup: SetupFn + #setup: SetupFn constructor() { super() - this.setup = (onFocus) => { + this.#setup = (onFocus) => { // addEventListener does not exist in React Native, but window does // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!isServer && window.addEventListener) { @@ -31,22 +31,22 @@ export class FocusManager extends Subscribable { } protected onSubscribe(): void { - if (!this.cleanup) { - this.setEventListener(this.setup) + if (!this.#cleanup) { + this.setEventListener(this.#setup) } } protected onUnsubscribe() { if (!this.hasListeners()) { - this.cleanup?.() - this.cleanup = undefined + this.#cleanup?.() + this.#cleanup = undefined } } setEventListener(setup: SetupFn): void { - this.setup = setup - this.cleanup?.() - this.cleanup = setup((focused) => { + this.#setup = setup + this.#cleanup?.() + this.#cleanup = setup((focused) => { if (typeof focused === 'boolean') { this.setFocused(focused) } else { @@ -56,7 +56,7 @@ export class FocusManager extends Subscribable { } setFocused(focused?: boolean): void { - this.focused = focused + this.#focused = focused if (focused) { this.onFocus() @@ -70,8 +70,8 @@ export class FocusManager extends Subscribable { } isFocused(): boolean { - if (typeof this.focused === 'boolean') { - return this.focused + if (typeof this.#focused === 'boolean') { + return this.#focused } // document global can be unavailable in react native diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index 3c705d286a..ede848cdd1 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -11,13 +11,7 @@ export { MutationObserver } from './mutationObserver' export { notifyManager } from './notifyManager' export { focusManager } from './focusManager' export { onlineManager } from './onlineManager' -export { - hashQueryKey, - replaceEqualDeep, - isError, - isServer, - keepPreviousData, -} from './utils' +export { hashKey, replaceEqualDeep, isServer, keepPreviousData } from './utils' export type { MutationFilters, QueryFilters, Updater } from './utils' export { isCancelledError } from './retryer' export { dehydrate, hydrate } from './hydration' diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index b621b4013b..b6989f0eb0 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -30,10 +30,10 @@ export function infiniteQueryBehavior< Object.defineProperty(object, 'signal', { enumerable: true, get: () => { - if (context.signal?.aborted) { + if (context.signal.aborted) { cancelled = true } else { - context.signal?.addEventListener('abort', () => { + context.signal.addEventListener('abort', () => { cancelled = true }) } @@ -73,7 +73,7 @@ export function infiniteQueryBehavior< return Promise.resolve(pages) } - const queryFnContext: QueryFunctionContext = { + const queryFnContext: Omit = { queryKey: context.queryKey, pageParam: param, meta: context.options.meta, @@ -81,7 +81,7 @@ export function infiniteQueryBehavior< addSignalProperty(queryFnContext) - const queryFnResult = queryFn(queryFnContext) + const queryFnResult = queryFn(queryFnContext as QueryFunctionContext) const promise = Promise.resolve(queryFnResult).then((page) => buildNewPages(pages, param, page, previous), diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index ea83d0b1b3..803787268d 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -66,18 +66,12 @@ interface ContinueAction { type: 'continue' } -interface SetStateAction { - type: 'setState' - state: MutationState -} - export type Action = | ContinueAction | ErrorAction | FailedAction | LoadingAction | PauseAction - | SetStateAction | SuccessAction // CLASS @@ -92,10 +86,10 @@ export class Mutation< options: MutationOptions mutationId: number - private observers: MutationObserver[] - private mutationCache: MutationCache - private logger: Logger - private retryer?: Retryer + #observers: MutationObserver[] + #mutationCache: MutationCache + #logger: Logger + #retryer?: Retryer constructor(config: MutationConfig) { super() @@ -105,9 +99,9 @@ export class Mutation< ...config.options, } this.mutationId = config.mutationId - this.mutationCache = config.mutationCache - this.logger = config.logger || defaultLogger - this.observers = [] + this.#mutationCache = config.mutationCache + this.#logger = config.logger || defaultLogger + this.#observers = [] this.state = config.state || getDefaultState() this.updateCacheTime(this.options.cacheTime) @@ -118,18 +112,14 @@ export class Mutation< return this.options.meta } - setState(state: MutationState): void { - this.dispatch({ type: 'setState', state }) - } - addObserver(observer: MutationObserver): void { - if (this.observers.indexOf(observer) === -1) { - this.observers.push(observer) + if (this.#observers.indexOf(observer) === -1) { + this.#observers.push(observer) // Stop the mutation from being garbage collected this.clearGcTimeout() - this.mutationCache.notify({ + this.#mutationCache.notify({ type: 'observerAdded', mutation: this, observer, @@ -138,11 +128,11 @@ export class Mutation< } removeObserver(observer: MutationObserver): void { - this.observers = this.observers.filter((x) => x !== observer) + this.#observers = this.#observers.filter((x) => x !== observer) this.scheduleGc() - this.mutationCache.notify({ + this.#mutationCache.notify({ type: 'observerRemoved', mutation: this, observer, @@ -150,26 +140,26 @@ export class Mutation< } protected optionalRemove() { - if (!this.observers.length) { + if (!this.#observers.length) { if (this.state.status === 'loading') { this.scheduleGc() } else { - this.mutationCache.remove(this) + this.#mutationCache.remove(this) } } } continue(): Promise { - if (this.retryer) { - this.retryer.continue() - return this.retryer.promise + if (this.#retryer) { + this.#retryer.continue() + return this.#retryer.promise } return this.execute() } async execute(): Promise { const executeMutation = () => { - this.retryer = createRetryer({ + this.#retryer = createRetryer({ fn: () => { if (!this.options.mutationFn) { return Promise.reject('No mutationFn found') @@ -177,34 +167,34 @@ export class Mutation< return this.options.mutationFn(this.state.variables!) }, onFail: (failureCount, error) => { - this.dispatch({ type: 'failed', failureCount, error }) + this.#dispatch({ type: 'failed', failureCount, error }) }, onPause: () => { - this.dispatch({ type: 'pause' }) + this.#dispatch({ type: 'pause' }) }, onContinue: () => { - this.dispatch({ type: 'continue' }) + this.#dispatch({ type: 'continue' }) }, retry: this.options.retry ?? 0, retryDelay: this.options.retryDelay, networkMode: this.options.networkMode, }) - return this.retryer.promise + return this.#retryer.promise } const restored = this.state.status === 'loading' try { if (!restored) { - this.dispatch({ type: 'loading', variables: this.options.variables! }) + this.#dispatch({ type: 'loading', variables: this.options.variables! }) // Notify cache callback - await this.mutationCache.config.onMutate?.( + await this.#mutationCache.config.onMutate?.( this.state.variables, this as Mutation, ) const context = await this.options.onMutate?.(this.state.variables!) if (context !== this.state.context) { - this.dispatch({ + this.#dispatch({ type: 'loading', context, variables: this.state.variables, @@ -214,7 +204,7 @@ export class Mutation< const data = await executeMutation() // Notify cache callback - await this.mutationCache.config.onSuccess?.( + await this.#mutationCache.config.onSuccess?.( data, this.state.variables, this.state.context, @@ -234,12 +224,12 @@ export class Mutation< this.state.context, ) - this.dispatch({ type: 'success', data }) + this.#dispatch({ type: 'success', data }) return data } catch (error) { try { // Notify cache callback - await this.mutationCache.config.onError?.( + await this.#mutationCache.config.onError?.( error, this.state.variables, this.state.context, @@ -247,7 +237,7 @@ export class Mutation< ) if (process.env.NODE_ENV !== 'production') { - this.logger.error(error) + this.#logger.error(error) } await this.options.onError?.( @@ -264,12 +254,12 @@ export class Mutation< ) throw error } finally { - this.dispatch({ type: 'error', error: error as TError }) + this.#dispatch({ type: 'error', error: error as TError }) } } } - private dispatch(action: Action): void { + #dispatch(action: Action): void { const reducer = ( state: MutationState, ): MutationState => { @@ -322,20 +312,15 @@ export class Mutation< isPaused: false, status: 'error', } - case 'setState': - return { - ...state, - ...action.state, - } } } this.state = reducer(this.state) notifyManager.batch(() => { - this.observers.forEach((observer) => { + this.#observers.forEach((observer) => { observer.onMutationUpdate(action) }) - this.mutationCache.notify({ + this.#mutationCache.notify({ mutation: this, type: 'updated', action, diff --git a/packages/query-core/src/mutationCache.ts b/packages/query-core/src/mutationCache.ts index 63cea3249f..3f2bf11540 100644 --- a/packages/query-core/src/mutationCache.ts +++ b/packages/query-core/src/mutationCache.ts @@ -75,16 +75,13 @@ type MutationCacheListener = (event: MutationCacheNotifyEvent) => void // CLASS export class MutationCache extends Subscribable { - config: MutationCacheConfig + #mutations: Mutation[] + #mutationId: number - private mutations: Mutation[] - private mutationId: number - - constructor(config?: MutationCacheConfig) { + constructor(public config: MutationCacheConfig = {}) { super() - this.config = config || {} - this.mutations = [] - this.mutationId = 0 + this.#mutations = [] + this.#mutationId = 0 } build( @@ -95,7 +92,7 @@ export class MutationCache extends Subscribable { const mutation = new Mutation({ mutationCache: this, logger: client.getLogger(), - mutationId: ++this.mutationId, + mutationId: ++this.#mutationId, options: client.defaultMutationOptions(options), state, defaultOptions: options.mutationKey @@ -109,25 +106,25 @@ export class MutationCache extends Subscribable { } add(mutation: Mutation): void { - this.mutations.push(mutation) + this.#mutations.push(mutation) this.notify({ type: 'added', mutation }) } remove(mutation: Mutation): void { - this.mutations = this.mutations.filter((x) => x !== mutation) + this.#mutations = this.#mutations.filter((x) => x !== mutation) this.notify({ type: 'removed', mutation }) } clear(): void { notifyManager.batch(() => { - this.mutations.forEach((mutation) => { + this.#mutations.forEach((mutation) => { this.remove(mutation) }) }) } getAll(): Mutation[] { - return this.mutations + return this.#mutations } find( @@ -137,11 +134,13 @@ export class MutationCache extends Subscribable { filters.exact = true } - return this.mutations.find((mutation) => matchMutation(filters, mutation)) + return this.#mutations.find((mutation) => matchMutation(filters, mutation)) } findAll(filters: MutationFilters): Mutation[] { - return this.mutations.filter((mutation) => matchMutation(filters, mutation)) + return this.#mutations.filter((mutation) => + matchMutation(filters, mutation), + ) } notify(event: MutationCacheNotifyEvent) { @@ -153,7 +152,7 @@ export class MutationCache extends Subscribable { } resumePausedMutations(): Promise { - const pausedMutations = this.mutations.filter((x) => x.state.isPaused) + const pausedMutations = this.#mutations.filter((x) => x.state.isPaused) return notifyManager.batch(() => pausedMutations.reduce( (promise, mutation) => diff --git a/packages/query-core/src/mutationObserver.ts b/packages/query-core/src/mutationObserver.ts index b392c127d6..f6156b461b 100644 --- a/packages/query-core/src/mutationObserver.ts +++ b/packages/query-core/src/mutationObserver.ts @@ -18,7 +18,6 @@ type MutationObserverListener = ( ) => void interface NotifyOptions { - listeners?: boolean onError?: boolean onSuccess?: boolean } @@ -35,15 +34,11 @@ export class MutationObserver< > { options!: MutationObserverOptions - private client: QueryClient - private currentResult!: MutationObserverResult< - TData, - TError, - TVariables, - TContext - > - private currentMutation?: Mutation - private mutateOptions?: MutateOptions + #client: QueryClient + #currentResult: MutationObserverResult = + undefined! + #currentMutation?: Mutation + #mutateOptions?: MutateOptions constructor( client: QueryClient, @@ -51,10 +46,10 @@ export class MutationObserver< ) { super() - this.client = client + this.#client = client this.setOptions(options) this.bindMethods() - this.updateResult() + this.#updateResult() } protected bindMethods(): void { @@ -66,11 +61,11 @@ export class MutationObserver< options?: MutationObserverOptions, ) { const prevOptions = this.options - this.options = this.client.defaultMutationOptions(options) + this.options = this.#client.defaultMutationOptions(options) if (!shallowEqualObjects(prevOptions, this.options)) { - this.client.getMutationCache().notify({ + this.#client.getMutationCache().notify({ type: 'observerOptionsUpdated', - mutation: this.currentMutation, + mutation: this.#currentMutation, observer: this, }) } @@ -78,17 +73,15 @@ export class MutationObserver< protected onUnsubscribe(): void { if (!this.listeners.length) { - this.currentMutation?.removeObserver(this) + this.#currentMutation?.removeObserver(this) } } onMutationUpdate(action: Action): void { - this.updateResult() + this.#updateResult() // Determine which callbacks to trigger - const notifyOptions: NotifyOptions = { - listeners: true, - } + const notifyOptions: NotifyOptions = {} if (action.type === 'success') { notifyOptions.onSuccess = true @@ -96,7 +89,7 @@ export class MutationObserver< notifyOptions.onError = true } - this.notify(notifyOptions) + this.#notify(notifyOptions) } getCurrentResult(): MutationObserverResult< @@ -105,39 +98,41 @@ export class MutationObserver< TVariables, TContext > { - return this.currentResult + return this.#currentResult } reset(): void { - this.currentMutation = undefined - this.updateResult() - this.notify({ listeners: true }) + this.#currentMutation = undefined + this.#updateResult() + this.#notify({}) } mutate( variables?: TVariables, options?: MutateOptions, ): Promise { - this.mutateOptions = options + this.#mutateOptions = options - if (this.currentMutation) { - this.currentMutation.removeObserver(this) + if (this.#currentMutation) { + this.#currentMutation.removeObserver(this) } - this.currentMutation = this.client.getMutationCache().build(this.client, { - ...this.options, - variables: - typeof variables !== 'undefined' ? variables : this.options.variables, - }) + this.#currentMutation = this.#client + .getMutationCache() + .build(this.#client, { + ...this.options, + variables: + typeof variables !== 'undefined' ? variables : this.options.variables, + }) - this.currentMutation.addObserver(this) + this.#currentMutation.addObserver(this) - return this.currentMutation.execute() + return this.#currentMutation.execute() } - private updateResult(): void { - const state = this.currentMutation - ? this.currentMutation.state + #updateResult(): void { + const state = this.#currentMutation + ? this.#currentMutation.state : getDefaultState() const result: MutationObserverBaseResult< @@ -155,7 +150,7 @@ export class MutationObserver< reset: this.reset, } - this.currentResult = result as MutationObserverResult< + this.#currentResult = result as MutationObserverResult< TData, TError, TVariables, @@ -163,43 +158,41 @@ export class MutationObserver< > } - private notify(options: NotifyOptions) { + #notify(options: NotifyOptions) { notifyManager.batch(() => { // First trigger the mutate callbacks - if (this.mutateOptions) { + if (this.#mutateOptions) { if (options.onSuccess) { - this.mutateOptions.onSuccess?.( - this.currentResult.data!, - this.currentResult.variables!, - this.currentResult.context!, + this.#mutateOptions.onSuccess?.( + this.#currentResult.data!, + this.#currentResult.variables!, + this.#currentResult.context!, ) - this.mutateOptions.onSettled?.( - this.currentResult.data!, + this.#mutateOptions.onSettled?.( + this.#currentResult.data!, null, - this.currentResult.variables!, - this.currentResult.context, + this.#currentResult.variables!, + this.#currentResult.context, ) } else if (options.onError) { - this.mutateOptions.onError?.( - this.currentResult.error!, - this.currentResult.variables!, - this.currentResult.context, + this.#mutateOptions.onError?.( + this.#currentResult.error!, + this.#currentResult.variables!, + this.#currentResult.context, ) - this.mutateOptions.onSettled?.( + this.#mutateOptions.onSettled?.( undefined, - this.currentResult.error, - this.currentResult.variables!, - this.currentResult.context, + this.#currentResult.error, + this.#currentResult.variables!, + this.#currentResult.context, ) } } // Then trigger the listeners - if (options.listeners) { - this.listeners.forEach((listener) => { - listener(this.currentResult) - }) - } + this.listeners.forEach((listener) => { + listener(this.#currentResult) + }) }) } } diff --git a/packages/query-core/src/onlineManager.ts b/packages/query-core/src/onlineManager.ts index dd5c7ce113..8d032d9fd8 100644 --- a/packages/query-core/src/onlineManager.ts +++ b/packages/query-core/src/onlineManager.ts @@ -6,14 +6,14 @@ type SetupFn = ( ) => (() => void) | undefined export class OnlineManager extends Subscribable { - private online?: boolean - private cleanup?: () => void + #online?: boolean + #cleanup?: () => void - private setup: SetupFn + #setup: SetupFn constructor() { super() - this.setup = (onOnline) => { + this.#setup = (onOnline) => { // addEventListener does not exist in React Native, but window does // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!isServer && window.addEventListener) { @@ -34,22 +34,22 @@ export class OnlineManager extends Subscribable { } protected onSubscribe(): void { - if (!this.cleanup) { - this.setEventListener(this.setup) + if (!this.#cleanup) { + this.setEventListener(this.#setup) } } protected onUnsubscribe() { if (!this.hasListeners()) { - this.cleanup?.() - this.cleanup = undefined + this.#cleanup?.() + this.#cleanup = undefined } } setEventListener(setup: SetupFn): void { - this.setup = setup - this.cleanup?.() - this.cleanup = setup((online?: boolean) => { + this.#setup = setup + this.#cleanup?.() + this.#cleanup = setup((online?: boolean) => { if (typeof online === 'boolean') { this.setOnline(online) } else { @@ -59,7 +59,7 @@ export class OnlineManager extends Subscribable { } setOnline(online?: boolean): void { - this.online = online + this.#online = online if (online) { this.onOnline() @@ -73,8 +73,8 @@ export class OnlineManager extends Subscribable { } isOnline(): boolean { - if (typeof this.online === 'boolean') { - return this.online + if (typeof this.#online === 'boolean') { + return this.#online } if ( diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index b3a7dbddae..25f4592dbb 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -1,4 +1,3 @@ -import { difference, replaceAt } from './utils' import { notifyManager } from './notifyManager' import type { QueryObserverOptions, @@ -10,23 +9,33 @@ import type { NotifyOptions } from './queryObserver' import { QueryObserver } from './queryObserver' import { Subscribable } from './subscribable' +function difference(array1: T[], array2: T[]): T[] { + return array1.filter((x) => array2.indexOf(x) === -1) +} + +function replaceAt(array: T[], index: number, value: T): T[] { + const copy = array.slice(0) + copy[index] = value + return copy +} + type QueriesObserverListener = (result: QueryObserverResult[]) => void export class QueriesObserver extends Subscribable { - private client: QueryClient - private result: QueryObserverResult[] - private queries: QueryObserverOptions[] - private observers: QueryObserver[] - private observersMap: Record + #client: QueryClient + #result: QueryObserverResult[] + #queries: QueryObserverOptions[] + #observers: QueryObserver[] + #observersMap: Record constructor(client: QueryClient, queries?: QueryObserverOptions[]) { super() - this.client = client - this.queries = [] - this.result = [] - this.observers = [] - this.observersMap = {} + this.#client = client + this.#queries = [] + this.#result = [] + this.#observers = [] + this.#observersMap = {} if (queries) { this.setQueries(queries) @@ -35,9 +44,9 @@ export class QueriesObserver extends Subscribable { protected onSubscribe(): void { if (this.listeners.length === 1) { - this.observers.forEach((observer) => { + this.#observers.forEach((observer) => { observer.subscribe((result) => { - this.onUpdate(observer, result) + this.#onUpdate(observer, result) }) }) } @@ -51,7 +60,7 @@ export class QueriesObserver extends Subscribable { destroy(): void { this.listeners = [] - this.observers.forEach((observer) => { + this.#observers.forEach((observer) => { observer.destroy() }) } @@ -60,12 +69,12 @@ export class QueriesObserver extends Subscribable { queries: QueryObserverOptions[], notifyOptions?: NotifyOptions, ): void { - this.queries = queries + this.#queries = queries notifyManager.batch(() => { - const prevObservers = this.observers + const prevObservers = this.#observers - const newObserverMatches = this.findMatchingObservers(this.queries) + const newObserverMatches = this.#findMatchingObservers(this.#queries) // set options for the new observers to notify of changes newObserverMatches.forEach((match) => @@ -87,9 +96,9 @@ export class QueriesObserver extends Subscribable { return } - this.observers = newObservers - this.observersMap = newObserversMap - this.result = newResult + this.#observers = newObservers + this.#observersMap = newObserversMap + this.#result = newResult if (!this.hasListeners()) { return @@ -101,38 +110,38 @@ export class QueriesObserver extends Subscribable { difference(newObservers, prevObservers).forEach((observer) => { observer.subscribe((result) => { - this.onUpdate(observer, result) + this.#onUpdate(observer, result) }) }) - this.notify() + this.#notify() }) } getCurrentResult(): QueryObserverResult[] { - return this.result + return this.#result } getQueries() { - return this.observers.map((observer) => observer.getCurrentQuery()) + return this.#observers.map((observer) => observer.getCurrentQuery()) } getObservers() { - return this.observers + return this.#observers } getOptimisticResult(queries: QueryObserverOptions[]): QueryObserverResult[] { - return this.findMatchingObservers(queries).map((match) => + return this.#findMatchingObservers(queries).map((match) => match.observer.getOptimisticResult(match.defaultedQueryOptions), ) } - private findMatchingObservers( + #findMatchingObservers( queries: QueryObserverOptions[], ): QueryObserverMatch[] { - const prevObservers = this.observers + const prevObservers = this.#observers const defaultedQueryOptions = queries.map((options) => - this.client.defaultQueryOptions(options), + this.#client.defaultQueryOptions(options), ) const matchingObservers: QueryObserverMatch[] = @@ -156,9 +165,11 @@ export class QueriesObserver extends Subscribable { ) const getObserver = (options: QueryObserverOptions): QueryObserver => { - const defaultedOptions = this.client.defaultQueryOptions(options) - const currentObserver = this.observersMap[defaultedOptions.queryHash!] - return currentObserver ?? new QueryObserver(this.client, defaultedOptions) + const defaultedOptions = this.#client.defaultQueryOptions(options) + const currentObserver = this.#observersMap[defaultedOptions.queryHash!] + return ( + currentObserver ?? new QueryObserver(this.#client, defaultedOptions) + ) } const newOrReusedObservers: QueryObserverMatch[] = unmatchedQueries.map( @@ -182,18 +193,18 @@ export class QueriesObserver extends Subscribable { .sort(sortMatchesByOrderOfQueries) } - private onUpdate(observer: QueryObserver, result: QueryObserverResult): void { - const index = this.observers.indexOf(observer) + #onUpdate(observer: QueryObserver, result: QueryObserverResult): void { + const index = this.#observers.indexOf(observer) if (index !== -1) { - this.result = replaceAt(this.result, index, result) - this.notify() + this.#result = replaceAt(this.#result, index, result) + this.#notify() } } - private notify(): void { + #notify(): void { notifyManager.batch(() => { this.listeners.forEach((listener) => { - listener(this.result) + listener(this.#result) }) }) } diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 0c3fbef51d..e8ad9eb5ad 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -1,4 +1,4 @@ -import { getAbortController, noop, replaceData, timeUntilStale } from './utils' +import { noop, replaceData, timeUntilStale } from './utils' import type { InitialDataFunction, QueryKey, @@ -59,7 +59,7 @@ export interface FetchContext< > { fetchFn: () => unknown | Promise fetchOptions?: FetchOptions - signal?: AbortSignal + signal: AbortSignal options: QueryOptions queryKey: TQueryKey state: QueryState @@ -147,46 +147,48 @@ export class Query< queryKey: TQueryKey queryHash: string options!: QueryOptions - initialState: QueryState - revertState?: QueryState state: QueryState isFetchingOptimistic?: boolean - private cache: QueryCache - private logger: Logger - private promise?: Promise - private retryer?: Retryer - private observers: QueryObserver[] - private defaultOptions?: QueryOptions - private abortSignalConsumed: boolean + #initialState: QueryState + #revertState?: QueryState + #cache: QueryCache + #logger: Logger + #promise?: Promise + #retryer?: Retryer + #observers: QueryObserver[] + #defaultOptions?: QueryOptions + #abortSignalConsumed: boolean + constructor(config: QueryConfig) { super() - this.abortSignalConsumed = false - this.defaultOptions = config.defaultOptions - this.setOptions(config.options) - this.observers = [] - this.cache = config.cache - this.logger = config.logger || defaultLogger + this.#abortSignalConsumed = false + this.#defaultOptions = config.defaultOptions + this.#setOptions(config.options) + this.#observers = [] + this.#cache = config.cache + this.#logger = config.logger || defaultLogger this.queryKey = config.queryKey this.queryHash = config.queryHash - this.initialState = config.state || getDefaultState(this.options) - this.state = this.initialState + this.#initialState = config.state || getDefaultState(this.options) + this.state = this.#initialState } get meta(): QueryMeta | undefined { return this.options.meta } - private setOptions( + + #setOptions( options?: QueryOptions, ): void { - this.options = { ...this.defaultOptions, ...options } + this.options = { ...this.#defaultOptions, ...options } this.updateCacheTime(this.options.cacheTime) } protected optionalRemove() { - if (!this.observers.length && this.state.fetchStatus === 'idle') { - this.cache.remove(this) + if (!this.#observers.length && this.state.fetchStatus === 'idle') { + this.#cache.remove(this) } } @@ -197,7 +199,7 @@ export class Query< const data = replaceData(this.state.data, newData, this.options) // Set data and mark it as cached - this.dispatch({ + this.#dispatch({ data, type: 'success', dataUpdatedAt: options?.updatedAt, @@ -211,12 +213,12 @@ export class Query< state: QueryState, setStateOptions?: SetStateOptions, ): void { - this.dispatch({ type: 'setState', state, setStateOptions }) + this.#dispatch({ type: 'setState', state, setStateOptions }) } cancel(options?: CancelOptions): Promise { - const promise = this.promise - this.retryer?.cancel(options) + const promise = this.#promise + this.#retryer?.cancel(options) return promise ? promise.then(noop).catch(noop) : Promise.resolve() } @@ -228,11 +230,13 @@ export class Query< reset(): void { this.destroy() - this.setState(this.initialState) + this.setState(this.#initialState) } isActive(): boolean { - return this.observers.some((observer) => observer.options.enabled !== false) + return this.#observers.some( + (observer) => observer.options.enabled !== false, + ) } isDisabled(): boolean { @@ -243,7 +247,7 @@ export class Query< return ( this.state.isInvalidated || !this.state.dataUpdatedAt || - this.observers.some((observer) => observer.getCurrentResult().isStale) + this.#observers.some((observer) => observer.getCurrentResult().isStale) ) } @@ -256,67 +260,67 @@ export class Query< } onFocus(): void { - const observer = this.observers.find((x) => x.shouldFetchOnWindowFocus()) + const observer = this.#observers.find((x) => x.shouldFetchOnWindowFocus()) if (observer) { observer.refetch({ cancelRefetch: false }) } // Continue fetch if currently paused - this.retryer?.continue() + this.#retryer?.continue() } onOnline(): void { - const observer = this.observers.find((x) => x.shouldFetchOnReconnect()) + const observer = this.#observers.find((x) => x.shouldFetchOnReconnect()) if (observer) { observer.refetch({ cancelRefetch: false }) } // Continue fetch if currently paused - this.retryer?.continue() + this.#retryer?.continue() } addObserver(observer: QueryObserver): void { - if (this.observers.indexOf(observer) === -1) { - this.observers.push(observer) + if (this.#observers.indexOf(observer) === -1) { + this.#observers.push(observer) // Stop the query from being garbage collected this.clearGcTimeout() - this.cache.notify({ type: 'observerAdded', query: this, observer }) + this.#cache.notify({ type: 'observerAdded', query: this, observer }) } } removeObserver(observer: QueryObserver): void { - if (this.observers.indexOf(observer) !== -1) { - this.observers = this.observers.filter((x) => x !== observer) + if (this.#observers.indexOf(observer) !== -1) { + this.#observers = this.#observers.filter((x) => x !== observer) - if (!this.observers.length) { + if (!this.#observers.length) { // If the transport layer does not support cancellation // we'll let the query continue so the result can be cached - if (this.retryer) { - if (this.abortSignalConsumed) { - this.retryer.cancel({ revert: true }) + if (this.#retryer) { + if (this.#abortSignalConsumed) { + this.#retryer.cancel({ revert: true }) } else { - this.retryer.cancelRetry() + this.#retryer.cancelRetry() } } this.scheduleGc() } - this.cache.notify({ type: 'observerRemoved', query: this, observer }) + this.#cache.notify({ type: 'observerRemoved', query: this, observer }) } } getObserversCount(): number { - return this.observers.length + return this.#observers.length } invalidate(): void { if (!this.state.isInvalidated) { - this.dispatch({ type: 'invalidate' }) + this.#dispatch({ type: 'invalidate' }) } } @@ -328,40 +332,40 @@ export class Query< if (this.state.dataUpdatedAt && fetchOptions?.cancelRefetch) { // Silently cancel current fetch if the user wants to cancel refetches this.cancel({ silent: true }) - } else if (this.promise) { + } else if (this.#promise) { // make sure that retries that were potentially cancelled due to unmounts can continue - this.retryer?.continueRetry() + this.#retryer?.continueRetry() // Return current promise if we are already fetching - return this.promise + return this.#promise } } // Update config if passed, otherwise the config from the last execution is used if (options) { - this.setOptions(options) + this.#setOptions(options) } // Use the options from the first observer with a query function if no function is found. // This can happen when the query is hydrated or created with setQueryData. if (!this.options.queryFn) { - const observer = this.observers.find((x) => x.options.queryFn) + const observer = this.#observers.find((x) => x.options.queryFn) if (observer) { - this.setOptions(observer.options) + this.#setOptions(observer.options) } } if (!Array.isArray(this.options.queryKey)) { if (process.env.NODE_ENV !== 'production') { - this.logger.error( + this.#logger.error( `As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']`, ) } } - const abortController = getAbortController() + const abortController = new AbortController() // Create query function context - const queryFnContext: QueryFunctionContext = { + const queryFnContext: Omit, 'signal'> = { queryKey: this.queryKey, pageParam: undefined, meta: this.meta, @@ -374,11 +378,8 @@ export class Query< Object.defineProperty(object, 'signal', { enumerable: true, get: () => { - if (abortController) { - this.abortSignalConsumed = true - return abortController.signal - } - return undefined + this.#abortSignalConsumed = true + return abortController.signal }, }) } @@ -390,12 +391,17 @@ export class Query< if (!this.options.queryFn) { return Promise.reject('Missing queryFn') } - this.abortSignalConsumed = false - return this.options.queryFn(queryFnContext) + this.#abortSignalConsumed = false + return this.options.queryFn( + queryFnContext as QueryFunctionContext, + ) } // Trigger behavior hook - const context: FetchContext = { + const context: Omit< + FetchContext, + 'signal' + > = { fetchOptions, options: this.options, queryKey: this.queryKey, @@ -405,23 +411,25 @@ export class Query< addSignalProperty(context) - this.options.behavior?.onFetch(context) + this.options.behavior?.onFetch( + context as FetchContext, + ) // Store state in case the current fetch needs to be reverted - this.revertState = this.state + this.#revertState = this.state // Set to fetching state if not already in it if ( this.state.fetchStatus === 'idle' || this.state.fetchMeta !== context.fetchOptions?.meta ) { - this.dispatch({ type: 'fetch', meta: context.fetchOptions?.meta }) + this.#dispatch({ type: 'fetch', meta: context.fetchOptions?.meta }) } const onError = (error: TError | { silent?: boolean }) => { // Optimistically update state if needed if (!(isCancelledError(error) && error.silent)) { - this.dispatch({ + this.#dispatch({ type: 'error', error: error as TError, }) @@ -429,10 +437,10 @@ export class Query< if (!isCancelledError(error)) { // Notify cache callback - this.cache.config.onError?.(error, this as Query) + this.#cache.config.onError?.(error, this as Query) if (process.env.NODE_ENV !== 'production') { - this.logger.error(error) + this.#logger.error(error) } } @@ -444,13 +452,13 @@ export class Query< } // Try to fetch the data - this.retryer = createRetryer({ + this.#retryer = createRetryer({ fn: context.fetchFn as () => TData, - abort: abortController?.abort.bind(abortController), + abort: abortController.abort.bind(abortController), onSuccess: (data) => { if (typeof data === 'undefined') { if (process.env.NODE_ENV !== 'production') { - this.logger.error( + this.#logger.error( `Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ${this.queryHash}`, ) } @@ -461,7 +469,7 @@ export class Query< this.setData(data as TData) // Notify cache callback - this.cache.config.onSuccess?.(data, this as Query) + this.#cache.config.onSuccess?.(data, this as Query) if (!this.isFetchingOptimistic) { // Schedule query gc after fetching @@ -471,25 +479,25 @@ export class Query< }, onError, onFail: (failureCount, error) => { - this.dispatch({ type: 'failed', failureCount, error }) + this.#dispatch({ type: 'failed', failureCount, error }) }, onPause: () => { - this.dispatch({ type: 'pause' }) + this.#dispatch({ type: 'pause' }) }, onContinue: () => { - this.dispatch({ type: 'continue' }) + this.#dispatch({ type: 'continue' }) }, retry: context.options.retry, retryDelay: context.options.retryDelay, networkMode: context.options.networkMode, }) - this.promise = this.retryer.promise + this.#promise = this.#retryer.promise - return this.promise + return this.#promise } - private dispatch(action: Action): void { + #dispatch(action: Action): void { const reducer = ( state: QueryState, ): QueryState => { @@ -542,8 +550,8 @@ export class Query< case 'error': const error = action.error as unknown - if (isCancelledError(error) && error.revert && this.revertState) { - return { ...this.revertState } + if (isCancelledError(error) && error.revert && this.#revertState) { + return { ...this.#revertState } } return { @@ -572,11 +580,11 @@ export class Query< this.state = reducer(this.state) notifyManager.batch(() => { - this.observers.forEach((observer) => { + this.#observers.forEach((observer) => { observer.onQueryUpdate(action) }) - this.cache.notify({ query: this, type: 'updated', action }) + this.#cache.notify({ query: this, type: 'updated', action }) }) } } diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index b124a8ba0f..4b827fcf67 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -68,7 +68,7 @@ type QueryCacheListener = (event: QueryCacheNotifyEvent) => void // CLASS export class QueryCache extends Subscribable { - private queries = new Map() + #queries = new Map() constructor(public config: QueryCacheConfig = {}) { super() @@ -101,8 +101,8 @@ export class QueryCache extends Subscribable { } add(query: Query): void { - if (!this.queries.has(query.queryHash)) { - this.queries.set(query.queryHash, query) + if (!this.#queries.has(query.queryHash)) { + this.#queries.set(query.queryHash, query) this.notify({ type: 'added', @@ -112,13 +112,13 @@ export class QueryCache extends Subscribable { } remove(query: Query): void { - const queryInMap = this.queries.get(query.queryHash) + const queryInMap = this.#queries.get(query.queryHash) if (queryInMap) { query.destroy() if (queryInMap === query) { - this.queries.delete(query.queryHash) + this.#queries.delete(query.queryHash) } this.notify({ type: 'removed', query }) @@ -141,13 +141,13 @@ export class QueryCache extends Subscribable { >( queryHash: string, ): Query | undefined { - return this.queries.get(queryHash) as + return this.#queries.get(queryHash) as | Query | undefined } getAll(): Query[] { - return [...this.queries.values()] + return [...this.#queries.values()] } find( diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 58033e7f80..7673ee1f56 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -1,6 +1,6 @@ import type { QueryFilters, Updater, MutationFilters } from './utils' import { - hashQueryKey, + hashKey, noop, partialMatchKey, hashQueryKeyByOptions, @@ -52,74 +52,74 @@ interface MutationDefaults { // CLASS export class QueryClient { - private queryCache: QueryCache - private mutationCache: MutationCache - private logger: Logger - private defaultOptions: DefaultOptions - private queryDefaults: QueryDefaults[] - private mutationDefaults: MutationDefaults[] - private mountCount: number - private unsubscribeFocus?: () => void - private unsubscribeOnline?: () => void + #queryCache: QueryCache + #mutationCache: MutationCache + #logger: Logger + #defaultOptions: DefaultOptions + #queryDefaults: QueryDefaults[] + #mutationDefaults: MutationDefaults[] + #mountCount: number + #unsubscribeFocus?: () => void + #unsubscribeOnline?: () => void constructor(config: QueryClientConfig = {}) { - this.queryCache = config.queryCache || new QueryCache() - this.mutationCache = config.mutationCache || new MutationCache() - this.logger = config.logger || defaultLogger - this.defaultOptions = config.defaultOptions || {} - this.queryDefaults = [] - this.mutationDefaults = [] - this.mountCount = 0 + this.#queryCache = config.queryCache || new QueryCache() + this.#mutationCache = config.mutationCache || new MutationCache() + this.#logger = config.logger || defaultLogger + this.#defaultOptions = config.defaultOptions || {} + this.#queryDefaults = [] + this.#mutationDefaults = [] + this.#mountCount = 0 if (process.env.NODE_ENV !== 'production' && config.logger) { - this.logger.error( + this.#logger.error( `Passing a custom logger has been deprecated and will be removed in the next major version.`, ) } } mount(): void { - this.mountCount++ - if (this.mountCount !== 1) return + this.#mountCount++ + if (this.#mountCount !== 1) return - this.unsubscribeFocus = focusManager.subscribe(() => { + this.#unsubscribeFocus = focusManager.subscribe(() => { if (focusManager.isFocused()) { this.resumePausedMutations() - this.queryCache.onFocus() + this.#queryCache.onFocus() } }) - this.unsubscribeOnline = onlineManager.subscribe(() => { + this.#unsubscribeOnline = onlineManager.subscribe(() => { if (onlineManager.isOnline()) { this.resumePausedMutations() - this.queryCache.onOnline() + this.#queryCache.onOnline() } }) } unmount(): void { - this.mountCount-- - if (this.mountCount !== 0) return + this.#mountCount-- + if (this.#mountCount !== 0) return - this.unsubscribeFocus?.() - this.unsubscribeFocus = undefined + this.#unsubscribeFocus?.() + this.#unsubscribeFocus = undefined - this.unsubscribeOnline?.() - this.unsubscribeOnline = undefined + this.#unsubscribeOnline?.() + this.#unsubscribeOnline = undefined } isFetching(filters: QueryFilters = {}): number { filters.fetchStatus = 'fetching' - return this.queryCache.findAll(filters).length + return this.#queryCache.findAll(filters).length } isMutating(filters?: MutationFilters): number { - return this.mutationCache.findAll({ ...filters, fetching: true }).length + return this.#mutationCache.findAll({ ...filters, fetching: true }).length } getQueryData( queryKey: QueryKey, ): TQueryFnData | undefined { - return this.queryCache.find({ queryKey })?.state.data + return this.#queryCache.find({ queryKey })?.state.data } ensureQueryData< @@ -151,7 +151,7 @@ export class QueryClient { updater: Updater, options?: SetDataOptions, ): TQueryFnData | undefined { - const query = this.queryCache.find({ queryKey }) + const query = this.#queryCache.find({ queryKey }) const prevData = query?.state.data const data = functionalUpdate(updater, prevData) @@ -167,7 +167,7 @@ export class QueryClient { QueryKey >({ queryKey }) - return this.queryCache + return this.#queryCache .build(this, defaultedOptions) .setData(data, { ...options, manual: true }) } @@ -190,11 +190,11 @@ export class QueryClient { getQueryState( queryKey: QueryKey, ): QueryState | undefined { - return this.queryCache.find({ queryKey })?.state + return this.#queryCache.find({ queryKey })?.state } removeQueries(filters?: QueryFilters): void { - const queryCache = this.queryCache + const queryCache = this.#queryCache notifyManager.batch(() => { queryCache.findAll(filters).forEach((query) => { queryCache.remove(query) @@ -206,7 +206,7 @@ export class QueryClient { filters?: ResetQueryFilters, options?: ResetOptions, ): Promise { - const queryCache = this.queryCache + const queryCache = this.#queryCache const refetchFilters: RefetchQueryFilters = { type: 'active', @@ -230,7 +230,7 @@ export class QueryClient { } const promises = notifyManager.batch(() => - this.queryCache + this.#queryCache .findAll(filters) .map((query) => query.cancel(cancelOptions)), ) @@ -243,7 +243,7 @@ export class QueryClient { options: InvalidateOptions = {}, ): Promise { return notifyManager.batch(() => { - this.queryCache.findAll(filters).forEach((query) => { + this.#queryCache.findAll(filters).forEach((query) => { query.invalidate() }) @@ -263,7 +263,7 @@ export class QueryClient { options?: RefetchOptions, ): Promise { const promises = notifyManager.batch(() => - this.queryCache + this.#queryCache .findAll(filters) .filter((query) => !query.isDisabled()) .map((query) => @@ -299,7 +299,7 @@ export class QueryClient { defaultedOptions.retry = false } - const query = this.queryCache.build(this, defaultedOptions) + const query = this.#queryCache.build(this, defaultedOptions) return query.isStaleByTime(defaultedOptions.staleTime) ? query.fetch(defaultedOptions) @@ -341,40 +341,40 @@ export class QueryClient { } resumePausedMutations(): Promise { - return this.mutationCache.resumePausedMutations() + return this.#mutationCache.resumePausedMutations() } getQueryCache(): QueryCache { - return this.queryCache + return this.#queryCache } getMutationCache(): MutationCache { - return this.mutationCache + return this.#mutationCache } getLogger(): Logger { - return this.logger + return this.#logger } getDefaultOptions(): DefaultOptions { - return this.defaultOptions + return this.#defaultOptions } setDefaultOptions(options: DefaultOptions): void { - this.defaultOptions = options + this.#defaultOptions = options } setQueryDefaults( queryKey: QueryKey, options: Omit, 'queryKey'>, ): void { - const result = this.queryDefaults.find( - (x) => hashQueryKey(queryKey) === hashQueryKey(x.queryKey), + const result = this.#queryDefaults.find( + (x) => hashKey(queryKey) === hashKey(x.queryKey), ) if (result) { result.defaultOptions = options } else { - this.queryDefaults.push({ queryKey, defaultOptions: options }) + this.#queryDefaults.push({ queryKey, defaultOptions: options }) } } @@ -386,19 +386,19 @@ export class QueryClient { } // Get the first matching defaults - const firstMatchingDefaults = this.queryDefaults.find((x) => + const firstMatchingDefaults = this.#queryDefaults.find((x) => partialMatchKey(queryKey, x.queryKey), ) // Additional checks and error in dev mode if (process.env.NODE_ENV !== 'production') { // Retrieve all matching defaults for the given key - const matchingDefaults = this.queryDefaults.filter((x) => + const matchingDefaults = this.#queryDefaults.filter((x) => partialMatchKey(queryKey, x.queryKey), ) // It is ok not having defaults, but it is error prone to have more than 1 default for a given key if (matchingDefaults.length > 1) { - this.logger.error( + this.#logger.error( `[QueryClient] Several query defaults match with key '${JSON.stringify( queryKey, )}'. The first matching query defaults are used. Please check how query defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetquerydefaults.`, @@ -413,13 +413,13 @@ export class QueryClient { mutationKey: MutationKey, options: MutationObserverOptions, ): void { - const result = this.mutationDefaults.find( - (x) => hashQueryKey(mutationKey) === hashQueryKey(x.mutationKey), + const result = this.#mutationDefaults.find( + (x) => hashKey(mutationKey) === hashKey(x.mutationKey), ) if (result) { result.defaultOptions = options } else { - this.mutationDefaults.push({ mutationKey, defaultOptions: options }) + this.#mutationDefaults.push({ mutationKey, defaultOptions: options }) } } @@ -431,19 +431,19 @@ export class QueryClient { } // Get the first matching defaults - const firstMatchingDefaults = this.mutationDefaults.find((x) => + const firstMatchingDefaults = this.#mutationDefaults.find((x) => partialMatchKey(mutationKey, x.mutationKey), ) // Additional checks and error in dev mode if (process.env.NODE_ENV !== 'production') { // Retrieve all matching defaults for the given key - const matchingDefaults = this.mutationDefaults.filter((x) => + const matchingDefaults = this.#mutationDefaults.filter((x) => partialMatchKey(mutationKey, x.mutationKey), ) // It is ok not having defaults, but it is error prone to have more than 1 default for a given key if (matchingDefaults.length > 1) { - this.logger.error( + this.#logger.error( `[QueryClient] Several mutation defaults match with key '${JSON.stringify( mutationKey, )}'. The first matching mutation defaults are used. Please check how mutation defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetmutationdefaults.`, @@ -488,7 +488,7 @@ export class QueryClient { } const defaultedOptions = { - ...this.defaultOptions.queries, + ...this.#defaultOptions.queries, ...this.getQueryDefaults(options?.queryKey), ...options, _defaulted: true, @@ -526,7 +526,7 @@ export class QueryClient { return options } return { - ...this.defaultOptions.mutations, + ...this.#defaultOptions.mutations, ...this.getMutationDefaults(options?.mutationKey), ...options, _defaulted: true, @@ -534,7 +534,7 @@ export class QueryClient { } clear(): void { - this.queryCache.clear() - this.mutationCache.clear() + this.#queryCache.clear() + this.#mutationCache.clear() } } diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 303e88ed08..66d1a421ef 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -28,7 +28,6 @@ type QueryObserverListener = ( ) => void export interface NotifyOptions { - cache?: boolean listeners?: boolean onError?: boolean onSuccess?: boolean @@ -53,26 +52,26 @@ export class QueryObserver< TQueryKey > - private client: QueryClient - private currentQuery!: Query - private currentQueryInitialState!: QueryState - private currentResult!: QueryObserverResult - private currentResultState?: QueryState - private currentResultOptions?: QueryObserverOptions< + #client: QueryClient + #currentQuery: Query = undefined! + #currentQueryInitialState: QueryState = undefined! + #currentResult: QueryObserverResult = undefined! + #currentResultState?: QueryState + #currentResultOptions?: QueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > - private previousQueryResult?: QueryObserverResult - private selectError: TError | null - private selectFn?: (data: TQueryData) => TData - private selectResult?: TData - private staleTimeoutId?: ReturnType - private refetchIntervalId?: ReturnType - private currentRefetchInterval?: number | false - private trackedProps!: Set + #previousQueryResult?: QueryObserverResult + #selectError: TError | null + #selectFn?: (data: TQueryData) => TData + #selectResult?: TData + #staleTimeoutId?: ReturnType + #refetchIntervalId?: ReturnType + #currentRefetchInterval?: number | false + #trackedProps: Set = new Set() constructor( client: QueryClient, @@ -86,10 +85,9 @@ export class QueryObserver< ) { super() - this.client = client + this.#client = client this.options = options - this.trackedProps = new Set() - this.selectError = null + this.#selectError = null this.bindMethods() this.setOptions(options) } @@ -100,13 +98,13 @@ export class QueryObserver< protected onSubscribe(): void { if (this.listeners.length === 1) { - this.currentQuery.addObserver(this) + this.#currentQuery.addObserver(this) - if (shouldFetchOnMount(this.currentQuery, this.options)) { - this.executeFetch() + if (shouldFetchOnMount(this.#currentQuery, this.options)) { + this.#executeFetch() } - this.updateTimers() + this.#updateTimers() } } @@ -118,7 +116,7 @@ export class QueryObserver< shouldFetchOnReconnect(): boolean { return shouldFetchOn( - this.currentQuery, + this.#currentQuery, this.options, this.options.refetchOnReconnect, ) @@ -126,7 +124,7 @@ export class QueryObserver< shouldFetchOnWindowFocus(): boolean { return shouldFetchOn( - this.currentQuery, + this.#currentQuery, this.options, this.options.refetchOnWindowFocus, ) @@ -134,9 +132,9 @@ export class QueryObserver< destroy(): void { this.listeners = [] - this.clearStaleTimeout() - this.clearRefetchInterval() - this.currentQuery.removeObserver(this) + this.#clearStaleTimeout() + this.#clearRefetchInterval() + this.#currentQuery.removeObserver(this) } setOptions( @@ -150,14 +148,14 @@ export class QueryObserver< notifyOptions?: NotifyOptions, ): void { const prevOptions = this.options - const prevQuery = this.currentQuery + const prevQuery = this.#currentQuery - this.options = this.client.defaultQueryOptions(options) + this.options = this.#client.defaultQueryOptions(options) if (!shallowEqualObjects(prevOptions, this.options)) { - this.client.getQueryCache().notify({ + this.#client.getQueryCache().notify({ type: 'observerOptionsUpdated', - query: this.currentQuery, + query: this.#currentQuery, observer: this, }) } @@ -174,7 +172,7 @@ export class QueryObserver< this.options.queryKey = prevOptions.queryKey } - this.updateQuery() + this.#updateQuery() const mounted = this.hasListeners() @@ -182,38 +180,38 @@ export class QueryObserver< if ( mounted && shouldFetchOptionally( - this.currentQuery, + this.#currentQuery, prevQuery, this.options, prevOptions, ) ) { - this.executeFetch() + this.#executeFetch() } // Update result - this.updateResult(notifyOptions) + this.#updateResult(notifyOptions) // Update stale interval if needed if ( mounted && - (this.currentQuery !== prevQuery || + (this.#currentQuery !== prevQuery || this.options.enabled !== prevOptions.enabled || this.options.staleTime !== prevOptions.staleTime) ) { - this.updateStaleTimeout() + this.#updateStaleTimeout() } - const nextRefetchInterval = this.computeRefetchInterval() + const nextRefetchInterval = this.#computeRefetchInterval() // Update refetch interval if needed if ( mounted && - (this.currentQuery !== prevQuery || + (this.#currentQuery !== prevQuery || this.options.enabled !== prevOptions.enabled || - nextRefetchInterval !== this.currentRefetchInterval) + nextRefetchInterval !== this.#currentRefetchInterval) ) { - this.updateRefetchInterval(nextRefetchInterval) + this.#updateRefetchInterval(nextRefetchInterval) } } @@ -226,13 +224,13 @@ export class QueryObserver< TQueryKey >, ): QueryObserverResult { - const query = this.client.getQueryCache().build(this.client, options) + const query = this.#client.getQueryCache().build(this.#client, options) return this.createResult(query, options) } getCurrentResult(): QueryObserverResult { - return this.currentResult + return this.#currentResult } trackResult( @@ -245,7 +243,7 @@ export class QueryObserver< configurable: false, enumerable: true, get: () => { - this.trackedProps.add(key as keyof QueryObserverResult) + this.#trackedProps.add(key as keyof QueryObserverResult) return result[key as keyof QueryObserverResult] }, }) @@ -255,7 +253,7 @@ export class QueryObserver< } getCurrentQuery(): Query { - return this.currentQuery + return this.#currentQuery } refetch({ @@ -279,11 +277,11 @@ export class QueryObserver< TQueryKey >, ): Promise> { - const defaultedOptions = this.client.defaultQueryOptions(options) + const defaultedOptions = this.#client.defaultQueryOptions(options) - const query = this.client + const query = this.#client .getQueryCache() - .build(this.client, defaultedOptions) + .build(this.#client, defaultedOptions) query.isFetchingOptimistic = true return query.fetch().then(() => this.createResult(query, defaultedOptions)) @@ -292,23 +290,23 @@ export class QueryObserver< protected fetch( fetchOptions: ObserverFetchOptions, ): Promise> { - return this.executeFetch({ + return this.#executeFetch({ ...fetchOptions, cancelRefetch: fetchOptions.cancelRefetch ?? true, }).then(() => { - this.updateResult() - return this.currentResult + this.#updateResult() + return this.#currentResult }) } - private executeFetch( + #executeFetch( fetchOptions?: ObserverFetchOptions, ): Promise { // Make sure we reference the latest query as the current one might have been removed - this.updateQuery() + this.#updateQuery() // Fetch - let promise: Promise = this.currentQuery.fetch( + let promise: Promise = this.#currentQuery.fetch( this.options as QueryOptions, fetchOptions, ) @@ -320,19 +318,19 @@ export class QueryObserver< return promise } - private updateStaleTimeout(): void { - this.clearStaleTimeout() + #updateStaleTimeout(): void { + this.#clearStaleTimeout() if ( isServer || - this.currentResult.isStale || + this.#currentResult.isStale || !isValidTimeout(this.options.staleTime) ) { return } const time = timeUntilStale( - this.currentResult.dataUpdatedAt, + this.#currentResult.dataUpdatedAt, this.options.staleTime, ) @@ -340,59 +338,62 @@ export class QueryObserver< // To mitigate this issue we always add 1 ms to the timeout. const timeout = time + 1 - this.staleTimeoutId = setTimeout(() => { - if (!this.currentResult.isStale) { - this.updateResult() + this.#staleTimeoutId = setTimeout(() => { + if (!this.#currentResult.isStale) { + this.#updateResult() } }, timeout) } - private computeRefetchInterval() { + #computeRefetchInterval() { return typeof this.options.refetchInterval === 'function' - ? this.options.refetchInterval(this.currentResult.data, this.currentQuery) + ? this.options.refetchInterval( + this.#currentResult.data, + this.#currentQuery, + ) : this.options.refetchInterval ?? false } - private updateRefetchInterval(nextInterval: number | false): void { - this.clearRefetchInterval() + #updateRefetchInterval(nextInterval: number | false): void { + this.#clearRefetchInterval() - this.currentRefetchInterval = nextInterval + this.#currentRefetchInterval = nextInterval if ( isServer || this.options.enabled === false || - !isValidTimeout(this.currentRefetchInterval) || - this.currentRefetchInterval === 0 + !isValidTimeout(this.#currentRefetchInterval) || + this.#currentRefetchInterval === 0 ) { return } - this.refetchIntervalId = setInterval(() => { + this.#refetchIntervalId = setInterval(() => { if ( this.options.refetchIntervalInBackground || focusManager.isFocused() ) { - this.executeFetch() + this.#executeFetch() } - }, this.currentRefetchInterval) + }, this.#currentRefetchInterval) } - private updateTimers(): void { - this.updateStaleTimeout() - this.updateRefetchInterval(this.computeRefetchInterval()) + #updateTimers(): void { + this.#updateStaleTimeout() + this.#updateRefetchInterval(this.#computeRefetchInterval()) } - private clearStaleTimeout(): void { - if (this.staleTimeoutId) { - clearTimeout(this.staleTimeoutId) - this.staleTimeoutId = undefined + #clearStaleTimeout(): void { + if (this.#staleTimeoutId) { + clearTimeout(this.#staleTimeoutId) + this.#staleTimeoutId = undefined } } - private clearRefetchInterval(): void { - if (this.refetchIntervalId) { - clearInterval(this.refetchIntervalId) - this.refetchIntervalId = undefined + #clearRefetchInterval(): void { + if (this.#refetchIntervalId) { + clearInterval(this.#refetchIntervalId) + this.#refetchIntervalId = undefined } } @@ -406,20 +407,20 @@ export class QueryObserver< TQueryKey >, ): QueryObserverResult { - const prevQuery = this.currentQuery + const prevQuery = this.#currentQuery const prevOptions = this.options - const prevResult = this.currentResult as + const prevResult = this.#currentResult as | QueryObserverResult | undefined - const prevResultState = this.currentResultState - const prevResultOptions = this.currentResultOptions + const prevResultState = this.#currentResultState + const prevResultOptions = this.#currentResultOptions const queryChange = query !== prevQuery const queryInitialState = queryChange ? query.state - : this.currentQueryInitialState + : this.#currentQueryInitialState const prevQueryResult = queryChange - ? this.currentResult - : this.previousQueryResult + ? this.#currentResult + : this.#previousQueryResult const { state } = query let { error, errorUpdatedAt, fetchStatus, status } = state @@ -454,21 +455,21 @@ export class QueryObserver< if ( prevResult && state.data === prevResultState?.data && - options.select === this.selectFn + options.select === this.#selectFn ) { - data = this.selectResult + data = this.#selectResult } else { try { - this.selectFn = options.select + this.#selectFn = options.select data = options.select(state.data) data = replaceData(prevResult?.data, data, options) - this.selectResult = data - this.selectError = null + this.#selectResult = data + this.#selectError = null } catch (selectError) { if (process.env.NODE_ENV !== 'production') { - this.client.getLogger().error(selectError) + this.#client.getLogger().error(selectError) } - this.selectError = selectError as TError + this.#selectError = selectError as TError } } } @@ -501,12 +502,12 @@ export class QueryObserver< if (options.select && typeof placeholderData !== 'undefined') { try { placeholderData = options.select(placeholderData) - this.selectError = null + this.#selectError = null } catch (selectError) { if (process.env.NODE_ENV !== 'production') { - this.client.getLogger().error(selectError) + this.#client.getLogger().error(selectError) } - this.selectError = selectError as TError + this.#selectError = selectError as TError } } } @@ -518,9 +519,9 @@ export class QueryObserver< } } - if (this.selectError) { - error = this.selectError as any - data = this.selectResult + if (this.#selectError) { + error = this.#selectError as any + data = this.#selectResult errorUpdatedAt = Date.now() status = 'error' } @@ -560,24 +561,24 @@ export class QueryObserver< return result as QueryObserverResult } - updateResult(notifyOptions?: NotifyOptions): void { - const prevResult = this.currentResult as + #updateResult(notifyOptions?: NotifyOptions): void { + const prevResult = this.#currentResult as | QueryObserverResult | undefined - const nextResult = this.createResult(this.currentQuery, this.options) - this.currentResultState = this.currentQuery.state - this.currentResultOptions = this.options + const nextResult = this.createResult(this.#currentQuery, this.options) + this.#currentResultState = this.#currentQuery.state + this.#currentResultOptions = this.options // Only notify and update result if something has changed if (shallowEqualObjects(nextResult, prevResult)) { return } - this.currentResult = nextResult + this.#currentResult = nextResult // Determine which callbacks to trigger - const defaultNotifyOptions: NotifyOptions = { cache: true } + const defaultNotifyOptions: NotifyOptions = {} const shouldNotifyListeners = (): boolean => { if (!prevResult) { @@ -588,20 +589,20 @@ export class QueryObserver< if ( notifyOnChangeProps === 'all' || - (!notifyOnChangeProps && !this.trackedProps.size) + (!notifyOnChangeProps && !this.#trackedProps.size) ) { return true } - const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) + const includedProps = new Set(notifyOnChangeProps ?? this.#trackedProps) if (this.options.throwErrors) { includedProps.add('error') } - return Object.keys(this.currentResult).some((key) => { + return Object.keys(this.#currentResult).some((key) => { const typedKey = key as keyof QueryObserverResult - const changed = this.currentResult[typedKey] !== prevResult[typedKey] + const changed = this.#currentResult[typedKey] !== prevResult[typedKey] return changed && includedProps.has(typedKey) }) } @@ -610,22 +611,22 @@ export class QueryObserver< defaultNotifyOptions.listeners = true } - this.notify({ ...defaultNotifyOptions, ...notifyOptions }) + this.#notify({ ...defaultNotifyOptions, ...notifyOptions }) } - private updateQuery(): void { - const query = this.client.getQueryCache().build(this.client, this.options) + #updateQuery(): void { + const query = this.#client.getQueryCache().build(this.#client, this.options) - if (query === this.currentQuery) { + if (query === this.#currentQuery) { return } - const prevQuery = this.currentQuery as + const prevQuery = this.#currentQuery as | Query | undefined - this.currentQuery = query - this.currentQueryInitialState = query.state - this.previousQueryResult = this.currentResult + this.#currentQuery = query + this.#currentQueryInitialState = query.state + this.#previousQueryResult = this.#currentResult if (this.hasListeners()) { prevQuery?.removeObserver(this) @@ -642,38 +643,36 @@ export class QueryObserver< notifyOptions.onError = true } - this.updateResult(notifyOptions) + this.#updateResult(notifyOptions) if (this.hasListeners()) { - this.updateTimers() + this.#updateTimers() } } - private notify(notifyOptions: NotifyOptions): void { + #notify(notifyOptions: NotifyOptions): void { notifyManager.batch(() => { // First trigger the configuration callbacks if (notifyOptions.onSuccess) { - this.options.onSuccess?.(this.currentResult.data!) - this.options.onSettled?.(this.currentResult.data!, null) + this.options.onSuccess?.(this.#currentResult.data!) + this.options.onSettled?.(this.#currentResult.data!, null) } else if (notifyOptions.onError) { - this.options.onError?.(this.currentResult.error!) - this.options.onSettled?.(undefined, this.currentResult.error!) + this.options.onError?.(this.#currentResult.error!) + this.options.onSettled?.(undefined, this.#currentResult.error!) } // Then trigger the listeners if (notifyOptions.listeners) { this.listeners.forEach((listener) => { - listener(this.currentResult) + listener(this.#currentResult) }) } // Then the cache listeners - if (notifyOptions.cache) { - this.client.getQueryCache().notify({ - query: this.currentQuery, - type: 'observerResultsUpdated', - }) - } + this.#client.getQueryCache().notify({ + query: this.#currentQuery, + type: 'observerResultsUpdated', + }) }) } } diff --git a/packages/query-core/src/removable.ts b/packages/query-core/src/removable.ts index 8d06cfc585..5b257fa6ea 100644 --- a/packages/query-core/src/removable.ts +++ b/packages/query-core/src/removable.ts @@ -2,7 +2,7 @@ import { isServer, isValidTimeout } from './utils' export abstract class Removable { cacheTime!: number - private gcTimeout?: ReturnType + #gcTimeout?: ReturnType destroy(): void { this.clearGcTimeout() @@ -12,7 +12,7 @@ export abstract class Removable { this.clearGcTimeout() if (isValidTimeout(this.cacheTime)) { - this.gcTimeout = setTimeout(() => { + this.#gcTimeout = setTimeout(() => { this.optionalRemove() }, this.cacheTime) } @@ -27,9 +27,9 @@ export abstract class Removable { } protected clearGcTimeout() { - if (this.gcTimeout) { - clearTimeout(this.gcTimeout) - this.gcTimeout = undefined + if (this.#gcTimeout) { + clearTimeout(this.#gcTimeout) + this.#gcTimeout = undefined } } diff --git a/packages/query-core/src/tests/focusManager.test.tsx b/packages/query-core/src/tests/focusManager.test.tsx index 2250aa23ee..68b6bdfcd5 100644 --- a/packages/query-core/src/tests/focusManager.test.tsx +++ b/packages/query-core/src/tests/focusManager.test.tsx @@ -63,26 +63,34 @@ describe('focusManager', () => { globalThis.document = document }) - test('cleanup should still be undefined if window is not defined', async () => { + test('cleanup (removeEventListener) should not be called if window is not defined', async () => { const restoreIsServer = setIsServer(true) + const removeEventListenerSpy = jest.spyOn(globalThis, 'removeEventListener') + const unsubscribe = focusManager.subscribe(() => undefined) - expect(focusManager['cleanup']).toBeUndefined() unsubscribe() + + expect(removeEventListenerSpy).not.toHaveBeenCalled() + restoreIsServer() }) - test('cleanup should still be undefined if window.addEventListener is not defined', async () => { + test('cleanup (removeEventListener) should not be called if window.addEventListener is not defined', async () => { const { addEventListener } = globalThis.window // @ts-expect-error globalThis.window.addEventListener = undefined + const removeEventListenerSpy = jest.spyOn(globalThis, 'removeEventListener') + const unsubscribe = focusManager.subscribe(() => undefined) - expect(focusManager['cleanup']).toBeUndefined() unsubscribe() + + expect(removeEventListenerSpy).not.toHaveBeenCalled() + globalThis.window.addEventListener = addEventListener }) diff --git a/packages/query-core/src/tests/mutationCache.test.tsx b/packages/query-core/src/tests/mutationCache.test.tsx index bb4f93c145..eeb7ec7c0d 100644 --- a/packages/query-core/src/tests/mutationCache.test.tsx +++ b/packages/query-core/src/tests/mutationCache.test.tsx @@ -241,46 +241,6 @@ describe('mutationCache', () => { }) }) - test('should only remove when the last observer unsubscribes', async () => { - const queryClient = createQueryClient() - const observer1 = new MutationObserver(queryClient, { - variables: 1, - cacheTime: 10, - mutationFn: async () => { - await sleep(10) - return 'update1' - }, - }) - - const observer2 = new MutationObserver(queryClient, { - cacheTime: 10, - mutationFn: async () => { - await sleep(10) - return 'update2' - }, - }) - - await observer1.mutate() - - // we currently have no way to add multiple observers to the same mutation - const currentMutation = observer1['currentMutation']! - currentMutation.addObserver(observer1) - currentMutation.addObserver(observer2) - - expect(currentMutation['observers'].length).toEqual(2) - expect(queryClient.getMutationCache().getAll()).toHaveLength(1) - - currentMutation.removeObserver(observer1) - currentMutation.removeObserver(observer2) - expect(currentMutation['observers'].length).toEqual(0) - expect(queryClient.getMutationCache().getAll()).toHaveLength(1) - // wait for cacheTime to gc - await sleep(10) - await waitFor(() => { - expect(queryClient.getMutationCache().getAll()).toHaveLength(0) - }) - }) - test('should be garbage collected later when unsubscribed and mutation is loading', async () => { const queryClient = createQueryClient() const onSuccess = jest.fn() diff --git a/packages/query-core/src/tests/mutationObserver.test.tsx b/packages/query-core/src/tests/mutationObserver.test.tsx index 12d1153ab6..1ca5345400 100644 --- a/packages/query-core/src/tests/mutationObserver.test.tsx +++ b/packages/query-core/src/tests/mutationObserver.test.tsx @@ -43,34 +43,4 @@ describe('mutationObserver', () => { // Clean-up unsubscribe2() }) - - test('should not notify listeners if options.listeners is set to false', async () => { - const mutation = new MutationObserver(queryClient, { - mutationFn: async (text: string) => { - await sleep(20) - return text - }, - }) - - const subscriptionHandler = jest.fn() - const unsubscribe = mutation.subscribe(subscriptionHandler) - mutation.mutate() - - await waitFor(() => { - // 2 calls: loading, success - expect(subscriptionHandler).toBeCalledTimes(2) - }) - subscriptionHandler.mockReset() - - // Force a notification with listeners set to false - // because there is no existing usage of notify with listeners set to false - mutation['notify']({ listeners: false }) - - await waitFor(() => { - // 0 call because no notification has been sent - expect(subscriptionHandler).toBeCalledTimes(0) - }) - - unsubscribe() - }) }) diff --git a/packages/query-core/src/tests/mutations.test.tsx b/packages/query-core/src/tests/mutations.test.tsx index 1d4604f912..b281259f3e 100644 --- a/packages/query-core/src/tests/mutations.test.tsx +++ b/packages/query-core/src/tests/mutations.test.tsx @@ -308,49 +308,24 @@ describe('mutations', () => { expect(onSettled).toHaveBeenCalled() }) - test('setState should update the mutation state', async () => { - const mutation = new MutationObserver(queryClient, { - mutationFn: async () => { - return 'update' - }, - onMutate: (text) => text, - }) - await mutation.mutate() - expect(mutation.getCurrentResult().data).toEqual('update') - - // Force setState usage - // because no use case has been found using mutation.setState - const currentMutation = mutation['currentMutation'] - currentMutation?.setState({ - context: undefined, - variables: undefined, - data: 'new', - error: null, - failureCount: 0, - failureReason: null, - isPaused: false, - status: 'success', - }) + test('addObserver should not add an existing observer', async () => { + const mutationCache = queryClient.getMutationCache() + const observer = new MutationObserver(queryClient, {}) + const currentMutation = mutationCache.build(queryClient, {}) - expect(mutation.getCurrentResult().data).toEqual('new') - }) + const fn = jest.fn() - test('addObserver should not add an existing observer', async () => { - const mutation = new MutationObserver(queryClient, { - mutationFn: async () => { - return 'update' - }, - onMutate: (text) => text, + const unsubscribe = mutationCache.subscribe((event) => { + fn(event.type) }) - await mutation.mutate() - // Force addObserver usage to add an existing observer - // because no use case has been found - const currentMutation = mutation['currentMutation']! - expect(currentMutation['observers'].length).toEqual(1) - currentMutation.addObserver(mutation) + currentMutation.addObserver(observer) + currentMutation.addObserver(observer) + + expect(fn).toHaveBeenCalledTimes(1) + expect(fn).toHaveBeenCalledWith('observerAdded') - expect(currentMutation['observers'].length).toEqual(1) + unsubscribe() }) test('mutate should throw an error if no mutationFn found', async () => { diff --git a/packages/query-core/src/tests/onlineManager.test.tsx b/packages/query-core/src/tests/onlineManager.test.tsx index c3a00e86d4..78cf936977 100644 --- a/packages/query-core/src/tests/onlineManager.test.tsx +++ b/packages/query-core/src/tests/onlineManager.test.tsx @@ -56,26 +56,34 @@ describe('onlineManager', () => { expect(remove2Spy).not.toHaveBeenCalled() }) - test('cleanup should still be undefined if window is not defined', async () => { + test('cleanup (removeEventListener) should not be called if window is not defined', async () => { const restoreIsServer = setIsServer(true) + const removeEventListenerSpy = jest.spyOn(globalThis, 'removeEventListener') + const unsubscribe = onlineManager.subscribe(() => undefined) - expect(onlineManager['cleanup']).toBeUndefined() unsubscribe() + + expect(removeEventListenerSpy).not.toHaveBeenCalled() + restoreIsServer() }) - test('cleanup should still be undefined if window.addEventListener is not defined', async () => { + test('cleanup (removeEventListener) should not be called if window.addEventListener is not defined', async () => { const { addEventListener } = globalThis.window // @ts-expect-error globalThis.window.addEventListener = undefined + const removeEventListenerSpy = jest.spyOn(globalThis, 'removeEventListener') + const unsubscribe = onlineManager.subscribe(() => undefined) - expect(onlineManager['cleanup']).toBeUndefined() unsubscribe() + + expect(removeEventListenerSpy).not.toHaveBeenCalled() + globalThis.window.addEventListener = addEventListener }) diff --git a/packages/query-core/src/tests/queriesObserver.test.tsx b/packages/query-core/src/tests/queriesObserver.test.tsx index 6f99192dc9..32487cd253 100644 --- a/packages/query-core/src/tests/queriesObserver.test.tsx +++ b/packages/query-core/src/tests/queriesObserver.test.tsx @@ -1,8 +1,7 @@ import { waitFor } from '@testing-library/react' import { sleep, queryKey, createQueryClient, mockLogger } from './utils' import type { QueryClient, QueryObserverResult } from '..' -import { QueriesObserver, QueryObserver } from '..' -import type { QueryKey } from '..' +import { QueriesObserver } from '..' describe('queriesObserver', () => { let queryClient: QueryClient @@ -289,39 +288,4 @@ describe('queriesObserver', () => { // Clean-up unsubscribe2() }) - - test('onUpdate should not update the result for an unknown observer', async () => { - const key1 = queryKey() - const key2 = queryKey() - - const queriesObserver = new QueriesObserver(queryClient, [ - { - queryKey: key1, - queryFn: () => 1, - }, - ]) - - const newQueryObserver = new QueryObserver< - unknown, - Error, - unknown, - unknown, - QueryKey - >(queryClient, { - queryKey: key2, - queryFn: () => 2, - }) - - // Force onUpdate with an unknown QueryObserver - // because no existing use case has been found in the lib - queriesObserver['onUpdate']( - newQueryObserver, - // The current queries observer result is re-used here - // to use a typescript friendly result - queriesObserver.getCurrentResult()[0]!, - ) - - // Should not alter the result - expect(queriesObserver.getCurrentResult()[-1]).toBeUndefined() - }) }) diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 8fb7298185..0faf7139b3 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -11,7 +11,7 @@ import type { QueryFunctionContext, QueryObserverResult, } from '..' -import { QueryObserver, isCancelledError, isError, onlineManager } from '..' +import { QueryObserver, isCancelledError, onlineManager } from '..' import { waitFor } from '@testing-library/react' describe('query', () => { @@ -245,7 +245,7 @@ describe('query', () => { queryKey: key, queryFn: async ({ signal }) => { await sleep(100) - return signal?.aborted ? 'aborted' : 'data' + return signal.aborted ? 'aborted' : 'data' }, }) @@ -282,15 +282,11 @@ describe('query', () => { let error queryFn.mockImplementation(async ({ signal }) => { - if (signal) { - signal.onabort = onAbort - signal.addEventListener('abort', abortListener) - } + signal.onabort = onAbort + signal.addEventListener('abort', abortListener) await sleep(10) - if (signal) { - signal.onabort = null - signal.removeEventListener('abort', abortListener) - } + signal.onabort = null + signal.removeEventListener('abort', abortListener) throw new Error() }) @@ -310,7 +306,7 @@ describe('query', () => { expect(queryFn).toHaveBeenCalledTimes(1) const signal = queryFn.mock.calls[0]![0].signal - expect(signal?.aborted).toBe(false) + expect(signal.aborted).toBe(false) expect(onAbort).not.toHaveBeenCalled() expect(abortListener).not.toHaveBeenCalled() @@ -318,7 +314,7 @@ describe('query', () => { await sleep(100) - expect(signal?.aborted).toBe(true) + expect(signal.aborted).toBe(true) expect(onAbort).toHaveBeenCalledTimes(1) expect(abortListener).toHaveBeenCalledTimes(1) expect(isCancelledError(error)).toBe(true) @@ -421,18 +417,19 @@ describe('query', () => { test('cancelling a rejected query should not have any effect', async () => { const key = queryKey() + const error = new Error('error') await queryClient.prefetchQuery({ queryKey: key, queryFn: async (): Promise => { - throw new Error('error') + throw error }, }) const query = queryCache.find({ queryKey: key })! query.cancel() await sleep(10) - expect(isError(query.state.error)).toBe(true) + expect(query.state.error).toBe(error) expect(isCancelledError(query.state.error)).toBe(false) }) @@ -702,7 +699,7 @@ describe('query', () => { notifySpy.mockRestore() }) - test('should not dispatch "invalidate" on invalidate() if already invalidated', async () => { + test('should not change state on invalidate() if already invalidated', async () => { const key = queryKey() await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data' }) @@ -711,19 +708,14 @@ describe('query', () => { query.invalidate() expect(query.state.isInvalidated).toBeTruthy() - const dispatchOriginal = query['dispatch'] - const dispatchSpy = jest.fn() - query['dispatch'] = dispatchSpy + const previousState = query.state query.invalidate() - expect(query.state.isInvalidated).toBeTruthy() - expect(dispatchSpy).not.toHaveBeenCalled() - - query['dispatch'] = dispatchOriginal + expect(query.state).toBe(previousState) }) - test('fetch should not dispatch "fetch" if state meta and fetchOptions meta are the same object', async () => { + test('fetch should not dispatch "fetch" query is already fetching', async () => { const key = queryKey() const queryFn = async () => { @@ -731,74 +723,31 @@ describe('query', () => { return 'data' } + const updates: Array = [] + await queryClient.prefetchQuery({ queryKey: key, queryFn }) const query = queryCache.find({ queryKey: key })! - const meta = { meta1: '1' } - - // This first fetch will set the state.meta value - query.fetch( - { - queryKey: key, - queryFn, - }, - { - meta, - }, - ) - - // Spy on private dispatch method - const dispatchOriginal = query['dispatch'] - const dispatchSpy = jest.fn() - query['dispatch'] = dispatchSpy - - // Second fetch in parallel with the same meta - query.fetch( - { - queryKey: key, - queryFn, - }, - { - meta, - // cancelRefetch must be set to true to enter in the case to test - // where isFetching is true - cancelRefetch: true, - }, - ) - - // Should not call dispatch with type set to fetch - expect(dispatchSpy).not.toHaveBeenCalledWith({ - meta, - type: 'fetch', + const unsubscribe = queryClient.getQueryCache().subscribe((event) => { + updates.push(event.type) }) - // Clean-up - await sleep(20) - query['dispatch'] = dispatchOriginal - }) - - test('fetch should not set the signal in the queryFnContext if AbortController is undefined', async () => { - const key = queryKey() - - // Mock the AbortController to be undefined - const AbortControllerOriginal = globalThis['AbortController'] - //@ts-expect-error - globalThis['AbortController'] = undefined + void query.fetch({ + queryKey: key, + queryFn, + }) - let signalTest: any - await queryClient.prefetchQuery({ + await query.fetch({ queryKey: key, - queryFn: ({ signal }) => { - signalTest = signal - return 'data' - }, + queryFn, }) - expect(signalTest).toBeUndefined() + expect(updates).toEqual([ + 'updated', // type: 'fetch' + 'updated', // type: 'success' + ]) - // Clean-up - //@ts-ignore - globalThis['AbortController'] = AbortControllerOriginal + unsubscribe() }) test('fetch should throw an error if the queryFn is not defined', async () => { @@ -845,47 +794,6 @@ describe('query', () => { unsubscribe() }) - test('fetch should dispatch fetch if is fetching and current promise is undefined', async () => { - const key = queryKey() - - const queryFn = async () => { - await sleep(10) - return 'data' - } - - await queryClient.prefetchQuery({ queryKey: key, queryFn }) - const query = queryCache.find({ queryKey: key })! - - query.fetch({ - queryKey: key, - queryFn, - }) - - // Force promise to undefined - // because no use case have been identified - query['promise'] = undefined - - // Spy on private dispatch method - const dispatchOriginal = query['dispatch'] - const dispatchSpy = jest.fn() - query['dispatch'] = dispatchSpy - - query.fetch({ - queryKey: key, - queryFn, - }) - - // Should call dispatch with type set to fetch - expect(dispatchSpy).toHaveBeenCalledWith({ - meta: undefined, - type: 'fetch', - }) - - // Clean-up - await sleep(20) - query['dispatch'] = dispatchOriginal - }) - test('constructor should call initialDataUpdatedAt if defined as a function', async () => { const key = queryKey() diff --git a/packages/query-core/src/tests/queryCache.test.tsx b/packages/query-core/src/tests/queryCache.test.tsx index 686ea06c6c..8d00b6cd39 100644 --- a/packages/query-core/src/tests/queryCache.test.tsx +++ b/packages/query-core/src/tests/queryCache.test.tsx @@ -1,7 +1,6 @@ import { sleep, queryKey, createQueryClient } from './utils' import { QueryClient } from '..' import { QueryCache, QueryObserver } from '..' -import type { Query } from '.././query' import { waitFor } from '@testing-library/react' describe('queryCache', () => { @@ -320,42 +319,14 @@ describe('queryCache', () => { describe('QueryCache.add', () => { test('should not try to add a query already added to the cache', async () => { const key = queryKey() - const hash = `["${key}"]` await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data1' }) - // Directly add the query from the cache - // to simulate a race condition - const query = queryCache['queries'].get(hash) as Query + const query = queryCache.findAll()[0]! const queryClone = Object.assign({}, query) - // No error should be thrown when trying to add the query queryCache.add(queryClone) expect(queryCache.getAll().length).toEqual(1) - - // Clean-up to avoid an error when queryClient.clear() - queryCache['queries'].delete(hash) - }) - - describe('QueryCache.remove', () => { - test('should not try to remove a query already removed from the cache', async () => { - const key = queryKey() - const hash = `["${key}"]` - - await queryClient.prefetchQuery({ - queryKey: key, - queryFn: () => 'data1', - }) - - // Directly remove the query from the cache - // to simulate a race condition - const query = queryCache['queries'].get(hash) as Query - const queryClone = Object.assign({}, query) - queryCache['queries'].delete(hash) - - // No error should be thrown when trying to remove the query - expect(() => queryCache.remove(queryClone)).not.toThrow() - }) }) }) }) diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 76a7e8612f..0dca06295a 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -1239,9 +1239,7 @@ describe('queryClient', () => { return new Promise((resolve) => { fetchCount++ setTimeout(() => resolve(5), 10) - if (signal) { - signal.addEventListener('abort', abortFn) - } + signal.addEventListener('abort', abortFn) }) }, initialData: 1, @@ -1264,9 +1262,7 @@ describe('queryClient', () => { return new Promise((resolve) => { fetchCount++ setTimeout(() => resolve(5), 10) - if (signal) { - signal.addEventListener('abort', abortFn) - } + signal.addEventListener('abort', abortFn) }) }, initialData: 1, diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index 5e30ff0431..a0d835c086 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -460,22 +460,26 @@ describe('queryObserver', () => { test('should clear interval when unsubscribing to a refetchInterval query', async () => { const key = queryKey() + let count = 0 - const fetchData = () => Promise.resolve('data') + const fetchData = () => { + count++ + Promise.resolve('data') + } const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: fetchData, cacheTime: 0, - refetchInterval: 1, + refetchInterval: 10, }) const unsubscribe = observer.subscribe(() => undefined) - // @ts-expect-error - expect(observer.refetchIntervalId).not.toBeUndefined() + expect(count).toBe(1) + await sleep(15) + expect(count).toBe(2) unsubscribe() - // @ts-expect-error - expect(observer.refetchIntervalId).toBeUndefined() await sleep(10) expect(queryClient.getQueryCache().find({ queryKey: key })).toBeUndefined() + expect(count).toBe(2) }) test('uses placeholderData as non-cache data when loading a query with no data', async () => { @@ -737,54 +741,6 @@ describe('queryObserver', () => { expect(observer.getCurrentResult().isPlaceholderData).toBe(false) }) - test('updateResult should not notify cache listeners if cache option is false', async () => { - const key = queryKey() - - const data1 = { value: 'data 1' } - const data2 = { value: 'data 2' } - - await queryClient.prefetchQuery({ queryKey: key, queryFn: () => data1 }) - const observer = new QueryObserver(queryClient, { - queryKey: key, - }) - await queryClient.prefetchQuery({ queryKey: key, queryFn: () => data2 }) - - const spy = jest.fn() - const unsubscribe = queryClient.getQueryCache().subscribe(spy) - observer.updateResult({ cache: false }) - - expect(spy).toHaveBeenCalledTimes(0) - - unsubscribe() - }) - - test('should not notify observer when the stale timeout expires and the current result is stale', async () => { - const key = queryKey() - const queryFn = () => 'data' - - await queryClient.prefetchQuery({ queryKey: key, queryFn }) - const observer = new QueryObserver(queryClient, { - queryKey: key, - queryFn, - staleTime: 20, - }) - - const spy = jest.fn() - const unsubscribe = observer.subscribe(spy) - await queryClient.refetchQueries({ queryKey: key }) - await sleep(10) - - // Force isStale to true - // because no use case has been found to reproduce this condition - // @ts-ignore - observer['currentResult'].isStale = true - spy.mockReset() - await sleep(30) - expect(spy).not.toHaveBeenCalled() - - unsubscribe() - }) - test('setOptions should notify cache listeners', async () => { const key = queryKey() diff --git a/packages/query-core/src/tests/utils.test.tsx b/packages/query-core/src/tests/utils.test.tsx index 8deefe47b5..104416bee8 100644 --- a/packages/query-core/src/tests/utils.test.tsx +++ b/packages/query-core/src/tests/utils.test.tsx @@ -1,6 +1,6 @@ import { replaceEqualDeep, - partialDeepEqual, + partialMatchKey, isPlainObject, matchMutation, scheduleMicrotask, @@ -66,47 +66,47 @@ describe('core/utils', () => { }) }) - describe('partialDeepEqual', () => { + describe('partialMatchKey', () => { it('should return `true` if a includes b', () => { - const a = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] } - const b = { a: { b: 'b' }, c: 'c', d: [] } - expect(partialDeepEqual(a, b)).toEqual(true) + const a = [{ a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }] + const b = [{ a: { b: 'b' }, c: 'c', d: [] }] + expect(partialMatchKey(a, b)).toEqual(true) }) it('should return `false` if a does not include b', () => { - const a = { a: { b: 'b' }, c: 'c', d: [] } - const b = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] } - expect(partialDeepEqual(a, b)).toEqual(false) + const a = [{ a: { b: 'b' }, c: 'c', d: [] }] + const b = [{ a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }] + expect(partialMatchKey(a, b)).toEqual(false) }) it('should return `true` if array a includes array b', () => { const a = [1, 2, 3] const b = [1, 2] - expect(partialDeepEqual(a, b)).toEqual(true) + expect(partialMatchKey(a, b)).toEqual(true) }) it('should return `false` if a is null and b is not', () => { - const a = null - const b = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] } - expect(partialDeepEqual(a, b)).toEqual(false) + const a = [null] + const b = [{ a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }] + expect(partialMatchKey(a, b)).toEqual(false) }) it('should return `false` if a contains null and b is not', () => { - const a = { a: null, c: 'c', d: [] } - const b = { a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] } - expect(partialDeepEqual(a, b)).toEqual(false) + const a = [{ a: null, c: 'c', d: [] }] + const b = [{ a: { b: 'b' }, c: 'c', d: [{ d: 'd ' }] }] + expect(partialMatchKey(a, b)).toEqual(false) }) it('should return `false` if b is null and a is not', () => { - const a = { a: { b: 'b' }, c: 'c', d: [] } - const b = null - expect(partialDeepEqual(a, b)).toEqual(false) + const a = [{ a: { b: 'b' }, c: 'c', d: [] }] + const b = [null] + expect(partialMatchKey(a, b)).toEqual(false) }) it('should return `false` if b contains null and a is not', () => { - const a = { a: { b: 'b' }, c: 'c', d: [] } - const b = { a: null, c: 'c', d: [{ d: 'd ' }] } - expect(partialDeepEqual(a, b)).toEqual(false) + const a = [{ a: { b: 'b' }, c: 'c', d: [] }] + const b = [{ a: null, c: 'c', d: [{ d: 'd ' }] }] + expect(partialMatchKey(a, b)).toEqual(false) }) }) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 8312473fd4..3103e31b6e 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -20,7 +20,7 @@ export interface QueryFunctionContext< TPageParam = any, > { queryKey: TQueryKey - signal?: AbortSignal + signal: AbortSignal pageParam?: TPageParam meta: QueryMeta | undefined } diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 0f16c10b20..9c18f71bf1 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -79,16 +79,6 @@ export function isValidTimeout(value: unknown): value is number { return typeof value === 'number' && value >= 0 && value !== Infinity } -export function difference(array1: T[], array2: T[]): T[] { - return array1.filter((x) => array2.indexOf(x) === -1) -} - -export function replaceAt(array: T[], index: number, value: T): T[] { - const copy = array.slice(0) - copy[index] = value - return copy -} - export function timeUntilStale(updatedAt: number, staleTime?: number): number { return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0) } @@ -106,7 +96,7 @@ export function matchQuery( stale, } = filters - if (isQueryKey(queryKey)) { + if (queryKey) { if (exact) { if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) { return false @@ -149,14 +139,12 @@ export function matchMutation( mutation: Mutation, ): boolean { const { exact, fetching, predicate, mutationKey } = filters - if (isQueryKey(mutationKey)) { + if (mutationKey) { if (!mutation.options.mutationKey) { return false } if (exact) { - if ( - hashQueryKey(mutation.options.mutationKey) !== hashQueryKey(mutationKey) - ) { + if (hashKey(mutation.options.mutationKey) !== hashKey(mutationKey)) { return false } } else if (!partialMatchKey(mutation.options.mutationKey, mutationKey)) { @@ -182,15 +170,15 @@ export function hashQueryKeyByOptions( queryKey: TQueryKey, options?: QueryOptions, ): string { - const hashFn = options?.queryKeyHashFn || hashQueryKey + const hashFn = options?.queryKeyHashFn || hashKey return hashFn(queryKey) } /** - * Default query keys hash function. + * Default query & mutation keys hash function. * Hashes the value into a stable hash. */ -export function hashQueryKey(queryKey: QueryKey): string { +export function hashKey(queryKey: QueryKey | MutationKey): string { return JSON.stringify(queryKey, (_, val) => isPlainObject(val) ? Object.keys(val) @@ -206,14 +194,8 @@ export function hashQueryKey(queryKey: QueryKey): string { /** * Checks if key `b` partially matches with key `a`. */ -export function partialMatchKey(a: QueryKey, b: QueryKey): boolean { - return partialDeepEqual(a, b) -} - -/** - * Checks if `b` partially matches with `a`. - */ -export function partialDeepEqual(a: any, b: any): boolean { +export function partialMatchKey(a: QueryKey, b: QueryKey): boolean +export function partialMatchKey(a: any, b: any): boolean { if (a === b) { return true } @@ -223,7 +205,7 @@ export function partialDeepEqual(a: any, b: any): boolean { } if (a && b && typeof a === 'object' && typeof b === 'object') { - return !Object.keys(b).some((key) => !partialDeepEqual(a[key], b[key])) + return !Object.keys(b).some((key) => !partialMatchKey(a[key], b[key])) } return false @@ -316,14 +298,6 @@ function hasObjectPrototype(o: any): boolean { return Object.prototype.toString.call(o) === '[object Object]' } -export function isQueryKey(value: unknown): value is QueryKey { - return Array.isArray(value) -} - -export function isError(value: any): value is Error { - return value instanceof Error -} - export function sleep(timeout: number): Promise { return new Promise((resolve) => { setTimeout(resolve, timeout) @@ -338,13 +312,6 @@ export function scheduleMicrotask(callback: () => void) { sleep(0).then(callback) } -export function getAbortController(): AbortController | undefined { - if (typeof AbortController === 'function') { - return new AbortController() - } - return -} - export function replaceData< TData, TOptions extends QueryOptions, diff --git a/packages/react-query-devtools/src/__tests__/Explorer.test.tsx b/packages/react-query-devtools/src/__tests__/Explorer.test.tsx index 7d4dd7e00e..cf5e15443c 100644 --- a/packages/react-query-devtools/src/__tests__/Explorer.test.tsx +++ b/packages/react-query-devtools/src/__tests__/Explorer.test.tsx @@ -90,6 +90,9 @@ describe('Explorer', () => { }) it('when the copy button is clicked but there is an error, show error state', async () => { + const consoleMock = jest.spyOn(console, 'error') + consoleMock.mockImplementation(() => undefined) + // Mock clipboard with error state Object.defineProperty(navigator, 'clipboard', { value: { @@ -117,6 +120,8 @@ describe('Explorer', () => { // Check that it has failed await screen.findByLabelText('Failed copying to clipboard') + expect(consoleMock).toHaveBeenCalled() + consoleMock.mockRestore() }) }) diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 88df4c4932..30d75fcfc2 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -493,11 +493,6 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByText('switch')) await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText(`data: ${key2}`)) - expect( - // @ts-expect-error - queryClient.getQueryCache().find({ queryKey: key2 })!.observers[0] - .listeners.length, - ).toBe(1) }) it('should retry fetch if the reset error boundary has been reset with global hook', async () => { diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index 0c7ce32a1b..aaa5fd480b 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -526,11 +526,6 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(screen.getByText('switch')) await waitFor(() => screen.getByText('Loading...')) await waitFor(() => screen.getByText(`data: ${key2}`)) - expect( - // @ts-expect-error - queryClient.getQueryCache().find({ queryKey: key2 })!.observers[0] - .listeners.length, - ).toBe(1) }) it('should throw errors to the error boundary by default', async () => { diff --git a/packages/vue-query/src/__tests__/utils.test.ts b/packages/vue-query/src/__tests__/utils.test.ts index 0b88356d4b..e78570b1b7 100644 --- a/packages/vue-query/src/__tests__/utils.test.ts +++ b/packages/vue-query/src/__tests__/utils.test.ts @@ -1,13 +1,7 @@ -import { isQueryKey, updateState, cloneDeep, cloneDeepUnref } from '../utils' +import { updateState, cloneDeep, cloneDeepUnref } from '../utils' import { reactive, ref } from 'vue-demi' describe('utils', () => { - describe('isQueryKey', () => { - test('should detect an array as query key', () => { - expect(isQueryKey(['string', 'array'])).toEqual(true) - }) - }) - describe('updateState', () => { test('should update first object with values from the second one', () => { const origin = { option1: 'a', option2: 'b', option3: 'c' } diff --git a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts index 5dfbbd9a80..9938e88e41 100644 --- a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts +++ b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts @@ -119,7 +119,7 @@ describe('VueQueryPlugin', () => { appMock._mixin.beforeCreate?.call(appMock) expect(appMock._provided).toMatchObject({ - VUE_QUERY_CLIENT: expect.objectContaining({ defaultOptions: {} }), + VUE_QUERY_CLIENT: expect.any(QueryClient), }) }) @@ -129,7 +129,7 @@ describe('VueQueryPlugin', () => { expect(appMock.provide).toHaveBeenCalledWith( VUE_QUERY_CLIENT, - expect.objectContaining({ defaultOptions: {} }), + expect.any(QueryClient), ) }) }) @@ -142,9 +142,7 @@ describe('VueQueryPlugin', () => { appMock._mixin.beforeCreate?.call(appMock) expect(appMock._provided).toMatchObject({ - [VUE_QUERY_CLIENT + ':CUSTOM']: expect.objectContaining({ - defaultOptions: {}, - }), + [VUE_QUERY_CLIENT + ':CUSTOM']: expect.any(QueryClient), }) }) @@ -154,7 +152,7 @@ describe('VueQueryPlugin', () => { expect(appMock.provide).toHaveBeenCalledWith( VUE_QUERY_CLIENT + ':CUSTOM', - expect.objectContaining({ defaultOptions: {} }), + expect.any(QueryClient), ) }) }) @@ -217,10 +215,10 @@ describe('VueQueryPlugin', () => { queryClientConfig: config, }) - expect(appMock.provide).toHaveBeenCalledWith( - VUE_QUERY_CLIENT, - expect.objectContaining(config), - ) + const client = (appMock.provide as jest.Mock).mock.calls[0][1] + const defaultOptions = client.getDefaultOptions() + + expect(defaultOptions).toEqual(config.defaultOptions) }, ) }) diff --git a/packages/vue-query/src/utils.ts b/packages/vue-query/src/utils.ts index a754fc477c..fc50f1ff26 100644 --- a/packages/vue-query/src/utils.ts +++ b/packages/vue-query/src/utils.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { QueryKey, MutationKey } from '@tanstack/query-core' import { isRef, unref } from 'vue-demi' import type { UnwrapRef } from 'vue-demi' @@ -10,14 +9,6 @@ export function getClientKey(key?: string) { return `${VUE_QUERY_CLIENT}${suffix}` } -export function isQueryKey(value: unknown): value is QueryKey { - return Array.isArray(value) -} - -export function isMutationKey(value: unknown): value is MutationKey { - return Array.isArray(value) -} - export function updateState( state: Record, update: Record, From 6d94cbdd4645d17f08829948f53f1f25da4dfe9c Mon Sep 17 00:00:00 2001 From: Prateek Surana Date: Sun, 15 Jan 2023 17:17:11 +0530 Subject: [PATCH 018/314] Rename `Hydrate` -> `HydrationBoundary` (#4821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :truck: Rename Hydrate -> HydrationBoundary and remove useHydrate * ✅ Update tests for HydrationBoundary * :sparkles: Update examples * 📝 Update v5 migration guide * ✏️ Fix typo * 📝 Update documentation to use HydrationBoundary Co-authored-by: Dominik Dorfmeister --- docs/react/guides/migrating-to-v5.md | 19 +++ docs/react/guides/ssr.md | 28 ++-- docs/react/reference/hydration.md | 39 +---- examples/react/nextjs/pages/_app.js | 6 +- .../{Hydrate.tsx => HydrationBoundary.tsx} | 24 ++- ...te.test.tsx => HydrationBoundary.test.tsx} | 141 +++++++++--------- packages/react-query/src/index.ts | 4 +- 7 files changed, 127 insertions(+), 134 deletions(-) rename packages/react-query/src/{Hydrate.tsx => HydrationBoundary.tsx} (76%) rename packages/react-query/src/__tests__/{Hydrate.test.tsx => HydrationBoundary.test.tsx} (66%) diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index d246ecde12..14f71e16ee 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -230,3 +230,22 @@ import { batch } from 'solid-js' notifyManager.setBatchNotifyFunction(batch) ``` + +### `Hydrate` has been renamed to `HydrationBoundary` and the `useHydrate` hook has been removed + +The `Hydrate` component has been renamed to `HydrationBoundary`. The `Hydrate` component was also a wrapper over `useHydrate` hook, which has been removed. + +```diff +- import { Hydrate } from '@tanstack/react-query' ++ import { HydrationBoundary } from '@tanstack/react-query' + + +- ++ + +- ++ +``` +``` + + diff --git a/docs/react/guides/ssr.md b/docs/react/guides/ssr.md index e825921abd..8cfa747a61 100644 --- a/docs/react/guides/ssr.md +++ b/docs/react/guides/ssr.md @@ -55,20 +55,20 @@ To support caching queries on the server and set up hydration: - Create a new `QueryClient` instance **inside of your app, and on an instance ref (or in React state). This ensures that data is not shared between different users and requests, while still only creating the QueryClient once per component lifecycle.** - Wrap your app component with `` and pass it the client instance -- Wrap your app component with `` and pass it the `dehydratedState` prop from `pageProps` +- Wrap your app component with `` and pass it the `dehydratedState` prop from `pageProps` ```tsx // _app.jsx -import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query' export default function MyApp({ Component, pageProps }) { const [queryClient] = React.useState(() => new QueryClient()) return ( - + - + ) } @@ -157,7 +157,7 @@ To support caching queries on the server and set up hydration: - Create a new `QueryClient` instance **inside of your app, and on an instance ref (or in React state). This ensures that data is not shared between different users and requests, while still only creating the QueryClient once per component lifecycle.** - Wrap your app component with `` and pass it the client instance -- Wrap your app component with `` and pass it the `dehydratedState` prop from `useDehydratedState()` +- Wrap your app component with `` and pass it the `dehydratedState` prop from `useDehydratedState()` ```bash npm i use-dehydrated-state @@ -169,7 +169,7 @@ yarn add use-dehydrated-state ```tsx // root.tsx -import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useDehydratedState } from 'use-dehydrated-state' @@ -180,9 +180,9 @@ export default function MyApp() { return ( - + - + ) } @@ -239,7 +239,7 @@ This guide is at-best, a high level overview of how SSR with React Query should > SECURITY NOTE: Serializing data with `JSON.stringify` can put you at risk for XSS-vulnerabilities, [this blog post explains why and how to solve it](https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0) ```tsx -import { dehydrate, Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { dehydrate, HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query'; async function handleRequest (req, res) { const queryClient = new QueryClient() @@ -248,9 +248,9 @@ async function handleRequest (req, res) { const html = ReactDOM.renderToString( - + - + ) @@ -276,7 +276,7 @@ async function handleRequest (req, res) { - Render your app with the client provider and also **using the dehydrated state. This is extremely important! You must render both server and client using the same dehydrated state to ensure hydration on the client produces the exact same markup as the server.** ```tsx -import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query' const dehydratedState = window.__REACT_QUERY_STATE__ @@ -284,9 +284,9 @@ const queryClient = new QueryClient() ReactDOM.hydrate( - + - + , document.getElementById('root') ) diff --git a/docs/react/reference/hydration.md b/docs/react/reference/hydration.md index 7b6b7defe6..15eff11628 100644 --- a/docs/react/reference/hydration.md +++ b/docs/react/reference/hydration.md @@ -5,7 +5,7 @@ title: hydration ## `dehydrate` -`dehydrate` creates a frozen representation of a `cache` that can later be hydrated with `Hydrate`, `useHydrate`, or `hydrate`. This is useful for passing prefetched queries from server to client or persisting queries to localStorage or other persistent locations. It only includes currently successful queries by default. +`dehydrate` creates a frozen representation of a `cache` that can later be hydrated with `HydrationBoundary` or `hydrate`. This is useful for passing prefetched queries from server to client or persisting queries to localStorage or other persistent locations. It only includes currently successful queries by default. ```tsx import { dehydrate } from '@tanstack/react-query' @@ -91,42 +91,17 @@ hydrate(queryClient, dehydratedState, options) If the queries included in dehydration already exist in the queryCache, `hydrate` does not overwrite them and they will be **silently** discarded. -[//]: # 'useHydrate' -## `useHydrate` +[//]: # 'HydrationBoundary' +## `HydrationBoundary` -`useHydrate` adds a previously dehydrated state into the `queryClient` that would be returned by `useQueryClient()`. If the client already contains data, the new queries will be intelligently merged based on update timestamp. +`HydrationBoundary` adds a previously dehydrated state into the `queryClient` that would be returned by `useQueryClient()`. If the client already contains data, the new queries will be intelligently merged based on update timestamp. ```tsx -import { useHydrate } from '@tanstack/react-query' - -useHydrate(dehydratedState, options) -``` - -**Options** - -- `dehydratedState: DehydratedState` - - **Required** - - The state to hydrate -- `options: HydrateOptions` - - Optional - - `defaultOptions: QueryOptions` - - The default query options to use for the hydrated queries. - - `context?: React.Context` - - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. - -[//]: # 'useHydrate' -[//]: # 'Hydrate' - -## `Hydrate` - -`Hydrate` wraps `useHydrate` into component. Can be useful when you need hydrate in class component or need hydrate on same level where `QueryClientProvider` rendered. - -```tsx -import { Hydrate } from '@tanstack/react-query' +import { HydrationBoundary } from '@tanstack/react-query' function App() { - return ... + return ... } ``` @@ -141,4 +116,4 @@ function App() { - `context?: React.Context` - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. -[//]: # 'Hydrate' +[//]: # 'HydrationBoundary' diff --git a/examples/react/nextjs/pages/_app.js b/examples/react/nextjs/pages/_app.js index fd46f9e3d9..3529f4e271 100644 --- a/examples/react/nextjs/pages/_app.js +++ b/examples/react/nextjs/pages/_app.js @@ -1,6 +1,6 @@ import React from 'react' import { - Hydrate, + HydrationBoundary, QueryClient, QueryClientProvider, } from '@tanstack/react-query' @@ -11,9 +11,9 @@ export default function MyApp({ Component, pageProps }) { return ( - + - + ) diff --git a/packages/react-query/src/Hydrate.tsx b/packages/react-query/src/HydrationBoundary.tsx similarity index 76% rename from packages/react-query/src/Hydrate.tsx rename to packages/react-query/src/HydrationBoundary.tsx index a1b9fed01f..a1e67673d3 100644 --- a/packages/react-query/src/Hydrate.tsx +++ b/packages/react-query/src/HydrationBoundary.tsx @@ -5,10 +5,17 @@ import { hydrate } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { ContextOptions } from './types' -export function useHydrate( - state: unknown, - options: HydrateOptions & ContextOptions = {}, -) { +export interface HydrationBoundaryProps { + state?: unknown + options?: HydrateOptions & ContextOptions + children?: React.ReactNode +} + +export const HydrationBoundary = ({ + children, + options = {}, + state, +}: HydrationBoundaryProps) => { const queryClient = useQueryClient({ context: options.context }) const optionsRef = React.useRef(options) @@ -23,15 +30,6 @@ export function useHydrate( hydrate(queryClient, state, optionsRef.current) } }, [queryClient, state]) -} - -export interface HydrateProps { - state?: unknown - options?: HydrateOptions - children?: React.ReactNode -} -export const Hydrate = ({ children, options, state }: HydrateProps) => { - useHydrate(state, options) return children as React.ReactElement } diff --git a/packages/react-query/src/__tests__/Hydrate.test.tsx b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx similarity index 66% rename from packages/react-query/src/__tests__/Hydrate.test.tsx rename to packages/react-query/src/__tests__/HydrationBoundary.test.tsx index c0219fc518..617605efbf 100644 --- a/packages/react-query/src/__tests__/Hydrate.test.tsx +++ b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx @@ -7,8 +7,7 @@ import { QueryCache, useQuery, dehydrate, - useHydrate, - Hydrate, + HydrationBoundary, } from '@tanstack/react-query' import { createQueryClient, sleep } from './utils' import * as coreModule from '@tanstack/query-core' @@ -32,75 +31,75 @@ describe('React hydration', () => { queryClient.clear() }) - describe('useHydrate', () => { - test('should hydrate queries to the cache on context', async () => { - const dehydratedState = JSON.parse(stringifiedState) - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) + test('should hydrate queries to the cache on context', async () => { + const dehydratedState = JSON.parse(stringifiedState) + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) - function Page() { - useHydrate(dehydratedState) - const { data } = useQuery({ - queryKey: ['string'], - queryFn: () => dataQuery(['string']), - }) - return ( -
    -

    {data}

    -
    - ) - } + function Page() { + const { data } = useQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['string']), + }) + return ( +
    +

    {data}

    +
    + ) + } - const rendered = render( - + const rendered = render( + + - , - ) + + , + ) - await rendered.findByText('stringCached') - await rendered.findByText('string') - queryClient.clear() - }) + await rendered.findByText('stringCached') + await rendered.findByText('string') + queryClient.clear() + }) - test('should hydrate queries to the cache on custom context', async () => { - const context = React.createContext(undefined) + test('should hydrate queries to the cache on custom context', async () => { + const context = React.createContext(undefined) - const queryCacheOuter = new QueryCache() - const queryCacheInner = new QueryCache() + const queryCacheOuter = new QueryCache() + const queryCacheInner = new QueryCache() - const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) - const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) + const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) + const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) - const dehydratedState = JSON.parse(stringifiedState) + const dehydratedState = JSON.parse(stringifiedState) - function Page() { - useHydrate(dehydratedState, { context }) - const { data } = useQuery({ - queryKey: ['string'], - queryFn: () => dataQuery(['string']), - context, - }) - return ( -
    -

    {data}

    -
    - ) - } + function Page() { + const { data } = useQuery({ + queryKey: ['string'], + queryFn: () => dataQuery(['string']), + context, + }) + return ( +
    +

    {data}

    +
    + ) + } - const rendered = render( - - + const rendered = render( + + + - - , - ) + + + , + ) - await rendered.findByText('stringCached') - await rendered.findByText('string') + await rendered.findByText('stringCached') + await rendered.findByText('string') - queryClientInner.clear() - queryClientOuter.clear() - }) + queryClientInner.clear() + queryClientOuter.clear() }) describe('ReactQueryCacheProvider with hydration support', () => { @@ -123,9 +122,9 @@ describe('React hydration', () => { const rendered = render( - + - + , ) @@ -148,10 +147,10 @@ describe('React hydration', () => { rendered.rerender( - + - + , ) @@ -184,9 +183,9 @@ describe('React hydration', () => { const rendered = render( - + - + , ) @@ -199,9 +198,9 @@ describe('React hydration', () => { rendered.rerender( - + - + , ) @@ -220,13 +219,14 @@ describe('React hydration', () => { const hydrateSpy = jest.spyOn(coreModule, 'hydrate') function Page() { - useHydrate(null) return null } render( - + + + , ) @@ -243,13 +243,14 @@ describe('React hydration', () => { const hydrateSpy = jest.spyOn(coreModule, 'hydrate') function Page() { - useHydrate(undefined) return null } render( - + + + , ) diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index f906c6d847..ea69b6cd2e 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -15,8 +15,8 @@ export { } from './QueryClientProvider' export type { QueryClientProviderProps } from './QueryClientProvider' export type { QueryErrorResetBoundaryProps } from './QueryErrorResetBoundary' -export { useHydrate, Hydrate } from './Hydrate' -export type { HydrateProps } from './Hydrate' +export { HydrationBoundary } from './HydrationBoundary' +export type { HydrationBoundaryProps } from './HydrationBoundary' export { QueryErrorResetBoundary, useQueryErrorResetBoundary, From 65af42c5106fa05953752440914a273713c14a4f Mon Sep 17 00:00:00 2001 From: "Mahmoud M. Anwar" Date: Fri, 20 Jan 2023 12:50:30 +0200 Subject: [PATCH 019/314] refactor: rename cacheTime to gcTime (#4829) * fix: rename cacheTime to gcTime * fix: wording of garbage collector to garbage collection * fix: tests to spy on setTimeout Since gcTimeout is private now, we need the tests spy on if the setTimeout is called * test: setTimeout is called when gcTimeout is not infinity * fix: useFakeTimers console warning BREAKING CHANGE --- docs/react/community/tkdodos-blog.md | 2 +- docs/react/guides/caching.md | 4 +- docs/react/guides/important-defaults.md | 2 +- docs/react/guides/migrating-to-v5.md | 20 +++++++- docs/react/guides/mutations.md | 2 +- docs/react/guides/prefetching.md | 2 +- docs/react/guides/ssr.md | 6 +-- .../plugins/createAsyncStoragePersister.md | 2 +- .../plugins/createSyncStoragePersister.md | 2 +- docs/react/plugins/persistQueryClient.md | 8 ++-- docs/react/reference/QueryClient.md | 2 +- docs/react/reference/useMutation.md | 4 +- docs/react/reference/useQuery.md | 6 +-- docs/vue/guides/mutations.md | 2 +- docs/vue/guides/ssr.md | 6 +-- examples/react/basic-typescript/src/index.tsx | 2 +- examples/react/offline/src/App.jsx | 2 +- examples/react/playground/src/index.jsx | 17 ++++--- examples/solid/basic-typescript/src/index.tsx | 2 +- examples/vue/persister/src/main.ts | 2 +- packages/query-core/src/mutation.ts | 2 +- packages/query-core/src/query.ts | 2 +- packages/query-core/src/removable.ts | 16 +++---- .../query-core/src/tests/hydration.test.tsx | 4 +- .../src/tests/mutationCache.test.tsx | 16 +++---- packages/query-core/src/tests/query.test.tsx | 22 ++++----- .../query-core/src/tests/queryClient.test.tsx | 16 +++---- .../src/tests/queryObserver.test.tsx | 2 +- packages/query-core/src/types.ts | 4 +- .../__tests__/QueryClientProvider.test.tsx | 4 +- .../src/__tests__/suspense.test.tsx | 4 +- .../src/__tests__/useMutation.test.tsx | 2 +- .../src/__tests__/useQuery.test.tsx | 47 +++++++++++++++---- .../__tests__/QueryClientProvider.test.tsx | 4 +- .../src/__tests__/createMutation.test.tsx | 2 +- .../src/__tests__/createQuery.test.tsx | 42 ++++++++++++++--- .../src/__tests__/suspense.test.tsx | 4 +- .../vue-query/src/__mocks__/useQueryClient.ts | 2 +- 38 files changed, 182 insertions(+), 108 deletions(-) diff --git a/docs/react/community/tkdodos-blog.md b/docs/react/community/tkdodos-blog.md index 174a969f1a..d567bdaa8e 100644 --- a/docs/react/community/tkdodos-blog.md +++ b/docs/react/community/tkdodos-blog.md @@ -8,7 +8,7 @@ React Query maintainer [TkDodo](https://twitter.com/tkdodo) has a series of blog ## [#1: Practical React Query](https://tkdodo.eu/blog/practical-react-query) -> An advanced introduction to React Query, showing practical tips that go beyond the docs. It covers explaining the defaults (`staleTime` vs. `cacheTime`), concepts like keeping server and client state separate, handling dependencies and creating custom hooks, as well as outlining why the `enabled` option is very powerful. [Read more...](https://tkdodo.eu/blog/practical-react-query) +> An advanced introduction to React Query, showing practical tips that go beyond the docs. It covers explaining the defaults (`staleTime` vs. `gcTime`), concepts like keeping server and client state separate, handling dependencies and creating custom hooks, as well as outlining why the `enabled` option is very powerful. [Read more...](https://tkdodo.eu/blog/practical-react-query) ## [#2: React Query Data Transformations](https://tkdodo.eu/blog/react-query-data-transformations) diff --git a/docs/react/guides/caching.md b/docs/react/guides/caching.md index bbaf0ee2aa..675168354f 100644 --- a/docs/react/guides/caching.md +++ b/docs/react/guides/caching.md @@ -14,7 +14,7 @@ This caching example illustrates the story and lifecycle of: - Inactive Queries - Garbage Collection -Let's assume we are using the default `cacheTime` of **5 minutes** and the default `staleTime` of `0`. +Let's assume we are using the default `gcTime` of **5 minutes** and the default `staleTime` of `0`. - A new instance of `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` mounts. - Since no other queries have been made with the `['todos']` query key, this query will show a hard loading state and make a network request to fetch the data. @@ -26,7 +26,7 @@ Let's assume we are using the default `cacheTime` of **5 minutes** and the defau - Note that regardless of whether both `fetchTodos` query functions are identical or not, both queries' [`status`](../reference/useQuery) are updated (including `isFetching`, `isLoading`, and other related values) because they have the same query key. - When the request completes successfully, the cache's data under the `['todos']` key is updated with the new data, and both instances are updated with the new data. - Both instances of the `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` query are unmounted and no longer in use. - - Since there are no more active instances of this query, a cache timeout is set using `cacheTime` to delete and garbage collect the query (defaults to **5 minutes**). + - Since there are no more active instances of this query, a garbage collection timeout is set using `gcTime` to delete and garbage collect the query (defaults to **5 minutes**). - Before the cache timeout has completed, another instance of `useQuery({ queryKey: ['todos'], queyFn: fetchTodos })` mounts. The query immediately returns the available cached data while the `fetchTodos` function is being run in the background. When it completes successfully, it will populate the cache with fresh data. - The final instance of `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` unmounts. - No more instances of `useQuery({ queyKey: ['todos'], queryFn: fetchTodos })` appear within **5 minutes**. diff --git a/docs/react/guides/important-defaults.md b/docs/react/guides/important-defaults.md index 57d867c60e..d2f78d68e0 100644 --- a/docs/react/guides/important-defaults.md +++ b/docs/react/guides/important-defaults.md @@ -20,7 +20,7 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul - Query results that have no more active instances of `useQuery`, `useInfiniteQuery` or query observers are labeled as "inactive" and remain in the cache in case they are used again at a later time. - By default, "inactive" queries are garbage collected after **5 minutes**. - > To change this, you can alter the default `cacheTime` for queries to something other than `1000 * 60 * 5` milliseconds. + > To change this, you can alter the default `gcTime` for queries to something other than `1000 * 60 * 5` milliseconds. - Queries that fail are **silently retried 3 times, with exponential backoff delay** before capturing and displaying an error to the UI. diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 14f71e16ee..f50c54419a 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -246,6 +246,24 @@ The `Hydrate` component has been renamed to `HydrationBoundary`. The `Hydrate` c - + ``` -``` +### Rename `cacheTime` to `gcTime` + +Almost everyone gets `cacheTime` wrong. It sounds like "the amount of time that data is cached for", but that is not correct. + +`cacheTime` does nothing as long as a query is still in used. It only kicks in as soon as the query becomes unused. After the time has passed, data will be "garbage collected" to avoid the cache from growing. + +`gc` is referring to "garbage collect" time. It's a bit more technical, but also a quite [well known abbreviation](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) in computer science. +```diff +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, ++ gcTime: 10 * MINUTE, + }, + }, +}) +``` \ No newline at end of file diff --git a/docs/react/guides/mutations.md b/docs/react/guides/mutations.md index 790396deb5..33e63b2249 100644 --- a/docs/react/guides/mutations.md +++ b/docs/react/guides/mutations.md @@ -355,7 +355,7 @@ const persister = createSyncStoragePersister({ const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/guides/prefetching.md b/docs/react/guides/prefetching.md index 1217a9f068..01a0865114 100644 --- a/docs/react/guides/prefetching.md +++ b/docs/react/guides/prefetching.md @@ -21,7 +21,7 @@ const prefetchTodos = async () => { - If data for this query is already in the cache and **not invalidated**, the data will not be fetched - If a `staleTime` is passed eg. `prefetchQuery({queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` and the data is older than the specified staleTime, the query will be fetched -- If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `cacheTime`. +- If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `gcTime`. ## Manually Priming a Query diff --git a/docs/react/guides/ssr.md b/docs/react/guides/ssr.md index 8cfa747a61..b1fa8a8e37 100644 --- a/docs/react/guides/ssr.md +++ b/docs/react/guides/ssr.md @@ -360,10 +360,10 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, React Query creates the isolated cache for this client, which is preserved in memory for the `cacheTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +In case you are creating the `QueryClient` for every request, React Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. -On the server, `cacheTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `cacheTime` then you will be responsible for clearing the cache early. +On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/QueryClient#queryclientclear) after the request is handled and dehydrated state has been sent to the client. -Alternatively, you can set a smaller `cacheTime`. +Alternatively, you can set a smaller `gcTime`. diff --git a/docs/react/plugins/createAsyncStoragePersister.md b/docs/react/plugins/createAsyncStoragePersister.md index d2107bce66..5812c32f3e 100644 --- a/docs/react/plugins/createAsyncStoragePersister.md +++ b/docs/react/plugins/createAsyncStoragePersister.md @@ -33,7 +33,7 @@ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persi const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/plugins/createSyncStoragePersister.md b/docs/react/plugins/createSyncStoragePersister.md index 46c28f1bb5..ea5243d701 100644 --- a/docs/react/plugins/createSyncStoragePersister.md +++ b/docs/react/plugins/createSyncStoragePersister.md @@ -31,7 +31,7 @@ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persist const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/plugins/persistQueryClient.md b/docs/react/plugins/persistQueryClient.md index dc2b2f00eb..e673c33813 100644 --- a/docs/react/plugins/persistQueryClient.md +++ b/docs/react/plugins/persistQueryClient.md @@ -13,11 +13,11 @@ This is set of utilities for interacting with "persisters" which save your query ## How It Works -**IMPORTANT** - for persist to work properly, you probably want to pass `QueryClient` a `cacheTime` value to override the default during hydration (as shown above). +**IMPORTANT** - for persist to work properly, you probably want to pass `QueryClient` a `gcTime` value to override the default during hydration (as shown above). If it is not set when creating the `QueryClient` instance, it will default to `300000` (5 minutes) for hydration, and the stored cache will be discarded after 5 minutes of inactivity. This is the default garbage collection behavior. -It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `cacheTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. +It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `gcTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. You can also pass it `Infinity` to disable garbage collection behavior entirely. @@ -25,7 +25,7 @@ You can also pass it `Infinity` to disable garbage collection behavior entirely. const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) @@ -187,7 +187,7 @@ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persist const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index 0453e71056..41a1bd4cf9 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -221,7 +221,7 @@ This distinction is more a "convenience" for ts devs that know which structure w ## `queryClient.setQueryData` -`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. +`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `gcTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. > The difference between using `setQueryData` and `fetchQuery` is that `setQueryData` is sync and assumes that you already synchronously have the data available. If you need to fetch the data asynchronously, it's suggested that you either refetch the query key or use `fetchQuery` to handle the asynchronous fetch. diff --git a/docs/react/reference/useMutation.md b/docs/react/reference/useMutation.md index eba51cdafb..0e32e637b3 100644 --- a/docs/react/reference/useMutation.md +++ b/docs/react/reference/useMutation.md @@ -20,7 +20,7 @@ const { status, } = useMutation({ mutationFn, - cacheTime, + gcTime, mutationKey, networkMode, onError, @@ -46,7 +46,7 @@ mutate(variables, { - **Required** - A function that performs an asynchronous task and returns a promise. - `variables` is an object that `mutate` will pass to your `mutationFn` -- `cacheTime: number | Infinity` +- `gcTime: number | Infinity` - The time in milliseconds that unused/inactive cache data remains in memory. When a mutation's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used. - If set to `Infinity`, will disable garbage collection - `mutationKey: string` diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index 175d102705..3039e8356f 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -29,7 +29,7 @@ const { } = useQuery({ queryKey, queryFn, - cacheTime, + gcTime, enabled, networkMode, initialData, @@ -91,9 +91,9 @@ const { - Defaults to `0` - The time in milliseconds after data is considered stale. This value only applies to the hook it is defined on. - If set to `Infinity`, the data will never be considered stale -- `cacheTime: number | Infinity` +- `gcTime: number | Infinity` - Defaults to `5 * 60 * 1000` (5 minutes) or `Infinity` during SSR - - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used. + - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used. - If set to `Infinity`, will disable garbage collection - `queryKeyHashFn: (queryKey: QueryKey) => string` - Optional diff --git a/docs/vue/guides/mutations.md b/docs/vue/guides/mutations.md index 7d0d5ebb34..f8e593b513 100644 --- a/docs/vue/guides/mutations.md +++ b/docs/vue/guides/mutations.md @@ -63,7 +63,7 @@ function addTodo() { const client = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/vue/guides/ssr.md b/docs/vue/guides/ssr.md index d0995a5b07..7a0098cd0c 100644 --- a/docs/vue/guides/ssr.md +++ b/docs/vue/guides/ssr.md @@ -213,10 +213,10 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `cacheTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. -On the server, `cacheTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `cacheTime` then you will be responsible for clearing the cache early. +On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/QueryClient#queryclientclear) after the request is handled and dehydrated state has been sent to the client. -Alternatively, you can set a smaller `cacheTime`. +Alternatively, you can set a smaller `gcTime`. diff --git a/examples/react/basic-typescript/src/index.tsx b/examples/react/basic-typescript/src/index.tsx index 903aa351b4..ca0f39faaf 100644 --- a/examples/react/basic-typescript/src/index.tsx +++ b/examples/react/basic-typescript/src/index.tsx @@ -10,7 +10,7 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }); diff --git a/examples/react/offline/src/App.jsx b/examples/react/offline/src/App.jsx index 094a9a0dcb..68a37eed6e 100644 --- a/examples/react/offline/src/App.jsx +++ b/examples/react/offline/src/App.jsx @@ -32,7 +32,7 @@ const location = new ReactLocation(); const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours staleTime: 2000, retry: 0, }, diff --git a/examples/react/playground/src/index.jsx b/examples/react/playground/src/index.jsx index a2d5123f38..46ed043a96 100644 --- a/examples/react/playground/src/index.jsx +++ b/examples/react/playground/src/index.jsx @@ -31,7 +31,7 @@ const queryClient = new QueryClient(); function Root() { const [staleTime, setStaleTime] = React.useState(1000); - const [cacheTime, setCacheTime] = React.useState(3000); + const [gcTime, setgcTime] = React.useState(3000); const [localErrorRate, setErrorRate] = React.useState(errorRate); const [localFetchTimeMin, setLocalFetchTimeMin] = React.useState(queryTimeMin); @@ -48,17 +48,16 @@ function Root() { queryClient.setDefaultOptions({ queries: { staleTime, - cacheTime, + gcTime, }, }); - }, [cacheTime, staleTime]); + }, [gcTime, staleTime]); return (

    - The "staleTime" and "cacheTime" durations have been altered in this - example to show how query stale-ness and query caching work on a - granular level + The "staleTime" and "gcTime" durations have been altered in this example + to show how query stale-ness and query caching work on a granular level

    Stale Time:{" "} @@ -72,13 +71,13 @@ function Root() { />
    - Cache Time:{" "} + Garbage collection Time:{" "} setCacheTime(parseFloat(e.target.value, 10))} + value={gcTime} + onChange={(e) => setgcTime(parseFloat(e.target.value, 10))} style={{ width: "100px" }} />
    diff --git a/examples/solid/basic-typescript/src/index.tsx b/examples/solid/basic-typescript/src/index.tsx index d4b79e1005..1334ac9a53 100644 --- a/examples/solid/basic-typescript/src/index.tsx +++ b/examples/solid/basic-typescript/src/index.tsx @@ -11,7 +11,7 @@ import { render } from 'solid-js/web' const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/examples/vue/persister/src/main.ts b/examples/vue/persister/src/main.ts index 61a93de4a0..11dcb67f7b 100644 --- a/examples/vue/persister/src/main.ts +++ b/examples/vue/persister/src/main.ts @@ -9,7 +9,7 @@ const vueQueryOptions: VueQueryPluginOptions = { queryClientConfig: { defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, + gcTime: 1000 * 60 * 60 * 24, staleTime: 1000 * 60 * 60 * 24, }, }, diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 803787268d..5e693a3ca9 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -104,7 +104,7 @@ export class Mutation< this.#observers = [] this.state = config.state || getDefaultState() - this.updateCacheTime(this.options.cacheTime) + this.updateGcTime(this.options.gcTime) this.scheduleGc() } diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index e8ad9eb5ad..3a1144b5f0 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -183,7 +183,7 @@ export class Query< ): void { this.options = { ...this.#defaultOptions, ...options } - this.updateCacheTime(this.options.cacheTime) + this.updateGcTime(this.options.gcTime) } protected optionalRemove() { diff --git a/packages/query-core/src/removable.ts b/packages/query-core/src/removable.ts index 5b257fa6ea..bf353266ca 100644 --- a/packages/query-core/src/removable.ts +++ b/packages/query-core/src/removable.ts @@ -1,7 +1,7 @@ import { isServer, isValidTimeout } from './utils' export abstract class Removable { - cacheTime!: number + gcTime!: number #gcTimeout?: ReturnType destroy(): void { @@ -11,18 +11,18 @@ export abstract class Removable { protected scheduleGc(): void { this.clearGcTimeout() - if (isValidTimeout(this.cacheTime)) { + if (isValidTimeout(this.gcTime)) { this.#gcTimeout = setTimeout(() => { this.optionalRemove() - }, this.cacheTime) + }, this.gcTime) } } - protected updateCacheTime(newCacheTime: number | undefined): void { - // Default to 5 minutes (Infinity for server-side) if no cache time is set - this.cacheTime = Math.max( - this.cacheTime || 0, - newCacheTime ?? (isServer ? Infinity : 5 * 60 * 1000), + protected updateGcTime(newGcTime: number | undefined): void { + // Default to 5 minutes (Infinity for server-side) if no gcTime is set + this.gcTime = Math.max( + this.gcTime || 0, + newGcTime ?? (isServer ? Infinity : 5 * 60 * 1000), ) } diff --git a/packages/query-core/src/tests/hydration.test.tsx b/packages/query-core/src/tests/hydration.test.tsx index 5b6601914e..2bf773b949 100644 --- a/packages/query-core/src/tests/hydration.test.tsx +++ b/packages/query-core/src/tests/hydration.test.tsx @@ -119,13 +119,13 @@ describe('dehydration and rehydration', () => { queryClient.clear() }) - test('should use the cache time from the client', async () => { + test('should use the garbage collection time from the client', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery({ queryKey: ['string'], queryFn: () => fetchData('string'), - cacheTime: 50, + gcTime: 50, }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) diff --git a/packages/query-core/src/tests/mutationCache.test.tsx b/packages/query-core/src/tests/mutationCache.test.tsx index eeb7ec7c0d..6636da10eb 100644 --- a/packages/query-core/src/tests/mutationCache.test.tsx +++ b/packages/query-core/src/tests/mutationCache.test.tsx @@ -199,14 +199,14 @@ describe('mutationCache', () => { }) describe('garbage collection', () => { - test('should remove unused mutations after cacheTime has elapsed', async () => { + test('should remove unused mutations after gcTime has elapsed', async () => { const testCache = new MutationCache() const testClient = createQueryClient({ mutationCache: testCache }) const onSuccess = jest.fn() await executeMutation(testClient, { mutationKey: ['a', 1], variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: () => Promise.resolve(), onSuccess, }) @@ -223,7 +223,7 @@ describe('mutationCache', () => { const queryClient = createQueryClient() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: () => Promise.resolve(), }) const unsubscribe = observer.subscribe(() => undefined) @@ -246,7 +246,7 @@ describe('mutationCache', () => { const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: async () => { await sleep(20) return 'data' @@ -258,22 +258,22 @@ describe('mutationCache', () => { unsubscribe() expect(queryClient.getMutationCache().getAll()).toHaveLength(1) await sleep(10) - // unsubscribe should not remove even though cacheTime has elapsed b/c mutation is still loading + // unsubscribe should not remove even though gcTime has elapsed b/c mutation is still loading expect(queryClient.getMutationCache().getAll()).toHaveLength(1) await sleep(10) - // should be removed after an additional cacheTime wait + // should be removed after an additional gcTime wait await waitFor(() => { expect(queryClient.getMutationCache().getAll()).toHaveLength(0) }) expect(onSuccess).toHaveBeenCalledTimes(1) }) - test('should call callbacks even with cacheTime 0 and mutation still loading', async () => { + test('should call callbacks even with gcTime 0 and mutation still loading', async () => { const queryClient = createQueryClient() const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 0, + gcTime: 0, mutationFn: async () => { return 'data' }, diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 0faf7139b3..145e79a8ed 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -28,25 +28,25 @@ describe('query', () => { queryClient.clear() }) - test('should use the longest cache time it has seen', async () => { + test('should use the longest garbage collection time it has seen', async () => { const key = queryKey() await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 100, + gcTime: 100, }) await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 200, + gcTime: 200, }) await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 10, + gcTime: 10, }) const query = queryCache.find({ queryKey: key })! - expect(query.cacheTime).toBe(200) + expect(query.gcTime).toBe(200) }) it('should continue retry after focus regain and resolve all promises', async () => { @@ -461,7 +461,7 @@ describe('query', () => { expect(query.state.status).toBe('error') }) - test('queries with cacheTime 0 should be removed immediately after unsubscribing', async () => { + test('queries with gcTime 0 should be removed immediately after unsubscribing', async () => { const key = queryKey() let count = 0 const observer = new QueryObserver(queryClient, { @@ -470,7 +470,7 @@ describe('query', () => { count++ return 'data' }, - cacheTime: 0, + gcTime: 0, staleTime: Infinity, }) const unsubscribe1 = observer.subscribe(() => undefined) @@ -492,7 +492,7 @@ describe('query', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: async () => 'data', - cacheTime: 0, + gcTime: 0, }) expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) @@ -511,7 +511,7 @@ describe('query', () => { await sleep(20) return 'data' }, - cacheTime: 10, + gcTime: 10, }) const unsubscribe = observer.subscribe(() => undefined) await sleep(20) @@ -519,7 +519,7 @@ describe('query', () => { observer.refetch() unsubscribe() await sleep(10) - // unsubscribe should not remove even though cacheTime has elapsed b/c query is still fetching + // unsubscribe should not remove even though gcTime has elapsed b/c query is still fetching expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(10) // should be removed after an additional staleTime wait @@ -533,7 +533,7 @@ describe('query', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: async () => 'data', - cacheTime: 0, + gcTime: 0, }) expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 0dca06295a..648cd9605b 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -43,14 +43,14 @@ describe('queryClient', () => { const testClient = createQueryClient({ defaultOptions: { - queries: { cacheTime: Infinity }, + queries: { gcTime: Infinity }, }, }) const fetchData = () => Promise.resolve('data') await testClient.prefetchQuery({ queryKey: key, queryFn: fetchData }) const newQuery = testClient.getQueryCache().find({ queryKey: key }) - expect(newQuery?.options.cacheTime).toBe(Infinity) + expect(newQuery?.options.gcTime).toBe(Infinity) }) test('should get defaultOptions', async () => { @@ -578,7 +578,7 @@ describe('queryClient', () => { expect(second).toBe(first) }) - test('should be able to fetch when cache time is set to 0 and then be removed', async () => { + test('should be able to fetch when garbage collection time is set to 0 and then be removed', async () => { const key1 = queryKey() const result = await queryClient.fetchQuery({ queryKey: key1, @@ -586,7 +586,7 @@ describe('queryClient', () => { await sleep(10) return 1 }, - cacheTime: 0, + gcTime: 0, }) expect(result).toEqual(1) await waitFor(() => @@ -594,7 +594,7 @@ describe('queryClient', () => { ) }) - test('should keep a query in cache if cache time is Infinity', async () => { + test('should keep a query in cache if garbage collection time is Infinity', async () => { const key1 = queryKey() const result = await queryClient.fetchQuery({ queryKey: key1, @@ -602,7 +602,7 @@ describe('queryClient', () => { await sleep(10) return 1 }, - cacheTime: Infinity, + gcTime: Infinity, }) const result2 = queryClient.getQueryData(key1) expect(result).toEqual(1) @@ -778,7 +778,7 @@ describe('queryClient', () => { expect(mockLogger.error).toHaveBeenCalled() }) - test('should be garbage collected after cacheTime if unused', async () => { + test('should be garbage collected after gcTime if unused', async () => { const key = queryKey() await queryClient.prefetchQuery({ @@ -786,7 +786,7 @@ describe('queryClient', () => { queryFn: async () => { return 'data' }, - cacheTime: 10, + gcTime: 10, }) expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(15) diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index a0d835c086..2e3458883d 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -469,7 +469,7 @@ describe('queryObserver', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: fetchData, - cacheTime: 0, + gcTime: 0, refetchInterval: 10, }) const unsubscribe = observer.subscribe(() => undefined) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 3103e31b6e..1019202c4a 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -77,7 +77,7 @@ export interface QueryOptions< retry?: RetryValue retryDelay?: RetryDelayValue networkMode?: NetworkMode - cacheTime?: number + gcTime?: number queryFn?: QueryFunction queryHash?: string queryKey?: TQueryKey @@ -577,7 +577,7 @@ export interface MutationOptions< retry?: RetryValue retryDelay?: RetryDelayValue networkMode?: NetworkMode - cacheTime?: number + gcTime?: number _defaulted?: boolean meta?: MutationMeta } diff --git a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx index bc1474a520..71a977acd0 100644 --- a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx @@ -113,7 +113,7 @@ describe('QueryClientProvider', () => { queryCache, defaultOptions: { queries: { - cacheTime: Infinity, + gcTime: Infinity, }, }, }) @@ -143,7 +143,7 @@ describe('QueryClientProvider', () => { await waitFor(() => rendered.getByText('test')) expect(queryCache.find({ queryKey: key })).toBeDefined() - expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) describe('with custom context', () => { diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 30d75fcfc2..3b7a1b53a5 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -938,7 +938,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('error boundary')) }) - it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { + it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { const key = queryKey() let state: UseQueryResult | null = null @@ -956,7 +956,7 @@ describe("useQuery's in Suspense mode", () => { return count }, suspense: true, - cacheTime: 0, + gcTime: 0, }) return ( diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 9f15b2e262..fccd24082a 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -860,7 +860,7 @@ describe('useMutation', () => { return count }, mutationKey, - cacheTime: 0, + gcTime: 0, onSuccess, onSettled, }) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 96ac6a1de9..09ecbad372 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -818,7 +818,7 @@ describe('useQuery', () => { expect(states[1]).toMatchObject({ data: 'data' }) }) - it('should pick up a query when re-mounting with cacheTime 0', async () => { + it('should pick up a query when re-mounting with gcTime 0', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -845,7 +845,7 @@ describe('useQuery', () => { return 'data: ' + value }, - cacheTime: 0, + gcTime: 0, notifyOnChangeProps: 'all', }) states.push(state) @@ -891,7 +891,7 @@ describe('useQuery', () => { }) }) - it('should not get into an infinite loop when removing a query with cacheTime 0 and rerendering', async () => { + it('should not get into an infinite loop when removing a query with gcTime 0 and rerendering', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -905,7 +905,7 @@ describe('useQuery', () => { return 'data' }, - cacheTime: 0, + gcTime: 0, notifyOnChangeProps: 'all', }) @@ -4009,14 +4009,14 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('status: loading, idle')) }) - test('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { + test('should not schedule garbage collection, if gcTimeout is set to `Infinity`', async () => { const key = queryKey() function Page() { const query = useQuery({ queryKey: key, queryFn: () => 'fetched data', - cacheTime: Infinity, + gcTime: Infinity, }) return
    {query.data}
    } @@ -4024,12 +4024,41 @@ describe('useQuery', () => { const rendered = renderWithClient(queryClient, ) await waitFor(() => rendered.getByText('fetched data')) + jest.useFakeTimers('legacy') + const setTimeoutSpy = jest.spyOn(globalThis.window, 'setTimeout') rendered.unmount() - const query = queryCache.find({ queryKey: key }) - // @ts-expect-error - expect(query!.cacheTimeout).toBe(undefined) + expect(setTimeoutSpy).not.toHaveBeenCalled() + jest.useRealTimers() + }) + + test('should schedule garbage collection, if gcTimeout is not set to infinity', async () => { + const key = queryKey() + + function Page() { + const query = useQuery({ + queryKey: key, + queryFn: () => 'fetched data', + gcTime: 1000 * 60 * 10, //10 Minutes + }) + return
    {query.data}
    + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('fetched data')) + + jest.useFakeTimers('legacy') + const setTimeoutSpy = jest.spyOn(globalThis.window, 'setTimeout') + + rendered.unmount() + + expect(setTimeoutSpy).toHaveBeenLastCalledWith( + expect.any(Function), + 1000 * 60 * 10, + ) + jest.useRealTimers() }) it('should not cause memo churn when data does not change', async () => { diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index d39848d0f2..a6cd97a235 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -111,7 +111,7 @@ describe('QueryClientProvider', () => { queryCache, defaultOptions: { queries: { - cacheTime: Infinity, + gcTime: Infinity, }, }, }) @@ -141,7 +141,7 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test')) expect(queryCache.find({ queryKey: key })).toBeDefined() - expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) describe('with custom context', () => { diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index b13a03606d..734176a080 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -955,7 +955,7 @@ describe('useMutation', () => { return count }, mutationKey: mutationKey, - cacheTime: 0, + gcTime: 0, onSuccess, onSettled, })) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 49eba723bf..c1d1400619 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -862,7 +862,7 @@ describe('createQuery', () => { expect(states[1]).toMatchObject({ data: 'data' }) }) - it('should pick up a query when re-mounting with cacheTime 0', async () => { + it('should pick up a query when re-mounting with gcTime 0', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -891,7 +891,7 @@ describe('createQuery', () => { await sleep(10) return 'data: ' + value }, - cacheTime: 0, + gcTime: 0, })) createRenderEffect(() => { states.push({ ...state }) @@ -3959,14 +3959,14 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('status: loading, idle')) }) - it('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { + it('should not schedule garbage collection, if gcTimeout is set to `Infinity`', async () => { const key = queryKey() function Page() { const query = createQuery(() => ({ queryKey: key, queryFn: () => 'fetched data', - cacheTime: Infinity, + gcTime: Infinity, })) return
    {query.data}
    } @@ -3978,12 +3978,40 @@ describe('createQuery', () => { )) await waitFor(() => screen.getByText('fetched data')) + const setTimeoutSpy = jest.spyOn(window, 'setTimeout') result.unmount() - const query = queryCache.find({ queryKey: key }) - // @ts-expect-error - expect(query!.cacheTimeout).toBe(undefined) + expect(setTimeoutSpy).not.toHaveBeenCalled() + }) + + it('should schedule garbage collection, if gcTimeout is not set to `Infinity`', async () => { + const key = queryKey() + + function Page() { + const query = createQuery(() => ({ + queryKey: key, + queryFn: () => 'fetched data', + gcTime: 1000 * 60 * 10, //10 Minutes + })) + return
    {query.data}
    + } + + const result = render(() => ( + + + + )) + + await waitFor(() => screen.getByText('fetched data')) + const setTimeoutSpy = jest.spyOn(window, 'setTimeout') + + result.unmount() + + expect(setTimeoutSpy).toHaveBeenLastCalledWith( + expect.any(Function), + 1000 * 60 * 10, + ) }) it('should not cause memo churn when data does not change', async () => { diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index aaa5fd480b..907a20d968 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -954,7 +954,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('error boundary')) }) - it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { + it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { const key = queryKey() let state: CreateQueryResult | null = null @@ -969,7 +969,7 @@ describe("useQuery's in Suspense mode", () => { await sleep(10) return count }, - cacheTime: 0, + gcTime: 0, })) createRenderEffect( diff --git a/packages/vue-query/src/__mocks__/useQueryClient.ts b/packages/vue-query/src/__mocks__/useQueryClient.ts index cd7484f8ba..65645ef45c 100644 --- a/packages/vue-query/src/__mocks__/useQueryClient.ts +++ b/packages/vue-query/src/__mocks__/useQueryClient.ts @@ -8,7 +8,7 @@ const queryClient = new QueryClient({ }, }, defaultOptions: { - queries: { retry: false, cacheTime: Infinity }, + queries: { retry: false, gcTime: Infinity }, }, }) From 63943c66cfeece65708534b05c40002cd7936db1 Mon Sep 17 00:00:00 2001 From: Damian Osipiuk Date: Fri, 20 Jan 2023 21:14:24 +0100 Subject: [PATCH 020/314] feat: replace custom context prop with custom queryClient instance (#4819) * feat: replace custom context prop with queryClient BREAKING CHANGE: custom context has been removed from hooks in favor of passing custom queryClient instance * fix: tests * fix: formatting * docs: migration guide entry * test: add custom queryClient tests Co-authored-by: Dominik Dorfmeister --- docs/config.json | 2 +- docs/react/guides/migrating-to-v5.md | 84 ++++++++---- .../src/__tests__/devtools.test.tsx | 111 ---------------- .../src/__tests__/utils.tsx | 4 +- .../react-query-devtools/src/devtools.tsx | 25 ++-- .../react-query/src/HydrationBoundary.tsx | 13 +- .../react-query/src/QueryClientProvider.tsx | 28 ++-- .../src/__tests__/HydrationBoundary.test.tsx | 7 +- .../__tests__/QueryClientProvider.test.tsx | 71 +--------- .../src/__tests__/useInfiniteQuery.test.tsx | 25 +++- .../src/__tests__/useIsFetching.test.tsx | 118 +++++------------ .../src/__tests__/useIsMutating.test.tsx | 98 ++++---------- .../src/__tests__/useMutation.test.tsx | 102 +++++--------- .../src/__tests__/useQueries.test.tsx | 110 ++++------------ .../src/__tests__/useQuery.test.tsx | 25 +++- packages/react-query/src/__tests__/utils.tsx | 11 +- packages/react-query/src/index.ts | 2 +- packages/react-query/src/types.ts | 48 +++---- packages/react-query/src/useBaseQuery.ts | 9 +- packages/react-query/src/useInfiniteQuery.ts | 4 +- packages/react-query/src/useIsFetching.ts | 15 +-- packages/react-query/src/useIsMutating.ts | 15 +-- packages/react-query/src/useMutation.ts | 6 +- packages/react-query/src/useQueries.ts | 17 +-- packages/react-query/src/useQuery.ts | 11 +- .../solid-query/src/QueryClientProvider.tsx | 31 ++--- .../__tests__/QueryClientProvider.test.tsx | 62 +-------- .../__tests__/createInfiniteQuery.test.tsx | 23 ++++ .../src/__tests__/createMutation.test.tsx | 102 +++++--------- .../src/__tests__/createQueries.test.tsx | 109 +++------------ .../src/__tests__/createQuery.test.tsx | 23 ++++ .../src/__tests__/useIsFetching.test.tsx | 124 +++++------------- .../src/__tests__/useIsMutating.test.tsx | 122 ++++------------- packages/solid-query/src/createBaseQuery.ts | 12 +- .../solid-query/src/createInfiniteQuery.ts | 4 +- packages/solid-query/src/createMutation.ts | 6 +- packages/solid-query/src/createQueries.ts | 9 +- packages/solid-query/src/createQuery.ts | 10 +- packages/solid-query/src/index.ts | 2 +- packages/solid-query/src/types.ts | 48 +++---- packages/solid-query/src/useIsFetching.ts | 9 +- packages/solid-query/src/useIsMutating.ts | 9 +- .../src/__tests__/useIsMutating.test.ts | 8 -- .../src/__tests__/useQueries.test.ts | 20 --- .../vue-query/src/__tests__/useQuery.test.ts | 14 +- .../src/__tests__/vueQueryPlugin.test.ts | 14 +- packages/vue-query/src/types.ts | 6 - packages/vue-query/src/useBaseQuery.ts | 27 ++-- packages/vue-query/src/useInfiniteQuery.ts | 23 ++-- packages/vue-query/src/useIsFetching.ts | 23 ++-- packages/vue-query/src/useIsMutating.ts | 19 +-- packages/vue-query/src/useMutation.ts | 26 ++-- packages/vue-query/src/useQueries.ts | 31 ++--- packages/vue-query/src/useQuery.ts | 22 +--- 54 files changed, 620 insertions(+), 1279 deletions(-) diff --git a/docs/config.json b/docs/config.json index af38944468..3cf527a1be 100644 --- a/docs/config.json +++ b/docs/config.json @@ -186,7 +186,7 @@ }, { "label": "Migrating to v5", - "to": "react/guides/migrating-v5" + "to": "react/guides/migrating-to-v5" } ] }, diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index f50c54419a..22dd2514c9 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -1,5 +1,5 @@ --- -id: migrating-to-react-query-5 +id: migrating-to-tanstack-query-5 title: Migrating to TanStack Query v5 --- @@ -117,12 +117,6 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. -### The `contextSharing` prop has been removed from QueryClientProvider - -You could previously use the `contextSharing` property to share the first (and at least one) instance of the query client context across the window. This ensured that if TanStack Query was used across different bundles or microfrontends then they will all use the same instance of the context, regardless of module scoping. - -However, isolation is often preferred for microfrontends. In v4 the option to pass a custom context to the `QueryClientProvider` was added, which allows exactly this. If you wish to use the same query client across multiple packages of an application, you can create a `QueryClient` in your application and then let the bundles share this through the `context` property of the `QueryClientProvider`. - ### The `isDataEqual` options has been removed from useQuery Previously, This function was used to indicate whether to use previous `data` (`true`) or new data (`false`) as a resolved data for the query. @@ -144,9 +138,30 @@ We have updated our browserslist to produce a more modern, performant and smalle TanStack Query has always had private fields and methods on classes, but they weren't really private - they were just private in `TypeScript`. We now use [ECMAScript Private class features](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields), which means those fields are now truly private and can't be accessed from the outside at runtime. -### The `useErrorBoundary` prop has been renamed to `throwErrors` +### Rename `cacheTime` to `gcTime` + +Almost everyone gets `cacheTime` wrong. It sounds like "the amount of time that data is cached for", but that is not correct. + +`cacheTime` does nothing as long as a query is still in used. It only kicks in as soon as the query becomes unused. After the time has passed, data will be "garbage collected" to avoid the cache from growing. + +`gc` is referring to "garbage collect" time. It's a bit more technical, but also a quite [well known abbreviation](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) in computer science. -To make the `useErrorBoundary` prop more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. +```diff +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, ++ gcTime: 10 * MINUTE, + }, + }, +}) +``` + +### The `useErrorBoundary` option has been renamed to `throwErrors` + +To make the `useErrorBoundary` option more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. ### `Error` is now the default type for errors instead of `unknown` @@ -216,6 +231,36 @@ There are some caveats to this change however, which you must be aware of: The `visibilitychange` event is used exclusively now. This is possible because we only support browsers that support the `visibilitychange` event. This fixes a bunch of issues [as listed here](https://github.com/TanStack/query/pull/4805). +### Removed custom `context` prop in favor of custom `queryClient` instance + +In v4, we introduced the possibility to pass a custom `context` to all react-query hooks. This allowed for proper isolation when using MicroFrontends. + +However, `context` is a react-only feature. All that `context` does is give us access to the `queryClient`. We could achieve the same isolation by allowing to pass in a custom `queryClient` directly. +This in turn will enable other frameworks to have the same functionality in a framework-agnostic way. + +```diff +import { queryClient } from './my-client' + +const { data } = useQuery( + { + queryKey: ['users', id], + queryFn: () => fetch(...), +- context: customContext + }, ++ queryClient, +) +``` + +[//]: # 'FrameworkBreakingChanges' + +## React Query Breaking Changes + +### The `contextSharing` prop has been removed from QueryClientProvider + +You could previously use the `contextSharing` property to share the first (and at least one) instance of the query client context across the window. This ensured that if TanStack Query was used across different bundles or microfrontends then they will all use the same instance of the context, regardless of module scoping. + +However, isolation is often preferred for microfrontends. In v4 the option to pass a custom context to the `QueryClientProvider` was added, which allows exactly this. If you wish to use the same query client across multiple packages of an application, you can create a `QueryClient` in your application and then let the bundles share this through the `context` property of the `QueryClientProvider`. + ### No longer using `unstable_batchedUpdates` as the batching function in React and React Native Since the function `unstable_batchedUpdates` is noop in React 18, it will no longer be automatically set as the batching function in `react-query`. @@ -247,23 +292,4 @@ The `Hydrate` component has been renamed to `HydrationBoundary`. The `Hydrate` c + ``` -### Rename `cacheTime` to `gcTime` - -Almost everyone gets `cacheTime` wrong. It sounds like "the amount of time that data is cached for", but that is not correct. - -`cacheTime` does nothing as long as a query is still in used. It only kicks in as soon as the query becomes unused. After the time has passed, data will be "garbage collected" to avoid the cache from growing. - -`gc` is referring to "garbage collect" time. It's a bit more technical, but also a quite [well known abbreviation](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) in computer science. - -```diff -const MINUTE = 1000 * 60; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { -- cacheTime: 10 * MINUTE, -+ gcTime: 10 * MINUTE, - }, - }, -}) -``` \ No newline at end of file +[//]: # 'FrameworkBreakingChanges' diff --git a/packages/react-query-devtools/src/__tests__/devtools.test.tsx b/packages/react-query-devtools/src/__tests__/devtools.test.tsx index 323a441fd6..888ce10093 100644 --- a/packages/react-query-devtools/src/__tests__/devtools.test.tsx +++ b/packages/react-query-devtools/src/__tests__/devtools.test.tsx @@ -1,8 +1,6 @@ import * as React from 'react' import { fireEvent, screen, waitFor, act } from '@testing-library/react' -import { ErrorBoundary } from 'react-error-boundary' import '@testing-library/jest-dom' -import type { QueryClient } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import { defaultPanelSize, sortFns } from '../utils' import { @@ -12,13 +10,6 @@ import { createQueryClient, } from './utils' -// TODO: This should be removed with the types for react-error-boundary get updated. -declare module 'react-error-boundary' { - interface ErrorBoundaryPropsWithFallback { - children: any - } -} - Object.defineProperty(window, 'matchMedia', { writable: true, value: jest.fn().mockImplementation((query: string) => ({ @@ -709,108 +700,6 @@ describe('ReactQueryDevtools', () => { await screen.findByRole('button', { name: /react query devtools/i }) }) - describe('with custom context', () => { - it('should render without error when the custom context aligns', async () => { - const context = React.createContext(undefined) - const { queryClient } = createQueryClient() - - function Page() { - const { data = 'default' } = useQuery({ - queryKey: ['check'], - queryFn: async () => 'test', - context, - }) - - return ( -
    -

    {data}

    -
    - ) - } - - renderWithClient(queryClient, , { - initialIsOpen: false, - context, - }) - - await screen.findByRole('button', { name: /open react query devtools/i }) - }) - - it('should render with error when the custom context is not passed to useQuery', async () => { - const consoleErrorMock = jest.spyOn(console, 'error') - consoleErrorMock.mockImplementation(() => undefined) - - const context = React.createContext(undefined) - const { queryClient } = createQueryClient() - - function Page() { - const { data = 'default' } = useQuery({ - queryKey: ['check'], - queryFn: async () => 'test', - throwErrors: true, - }) - - return ( -
    -

    {data}

    -
    - ) - } - - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { - initialIsOpen: false, - context, - }, - ) - - await waitFor(() => rendered.getByText('error boundary')) - - consoleErrorMock.mockRestore() - }) - - it('should render with error when the custom context is not passed to ReactQueryDevtools', async () => { - const consoleErrorMock = jest.spyOn(console, 'error') - consoleErrorMock.mockImplementation(() => undefined) - - const context = React.createContext(undefined) - const { queryClient } = createQueryClient() - - function Page() { - const { data = 'default' } = useQuery({ - queryKey: ['check'], - queryFn: async () => 'test', - throwErrors: true, - context, - }) - - return ( -
    -

    {data}

    -
    - ) - } - - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { - initialIsOpen: false, - }, - ) - - await waitFor(() => rendered.getByText('error boundary')) - - consoleErrorMock.mockRestore() - }) - }) - it('should render a menu to select panel position', async () => { const { queryClient } = createQueryClient() diff --git a/packages/react-query-devtools/src/__tests__/utils.tsx b/packages/react-query-devtools/src/__tests__/utils.tsx index 197b782afd..bff9c1d945 100644 --- a/packages/react-query-devtools/src/__tests__/utils.tsx +++ b/packages/react-query-devtools/src/__tests__/utils.tsx @@ -15,7 +15,7 @@ export function renderWithClient( renderOptions?: RenderOptions, ): ReturnType { const { rerender, ...result } = render( - + {ui} , @@ -25,7 +25,7 @@ export function renderWithClient( ...result, rerender: (rerenderUi: React.ReactElement) => rerender( - + {rerenderUi} , diff --git a/packages/react-query-devtools/src/devtools.tsx b/packages/react-query-devtools/src/devtools.tsx index 8067984722..d3e9ef2ba6 100644 --- a/packages/react-query-devtools/src/devtools.tsx +++ b/packages/react-query-devtools/src/devtools.tsx @@ -3,7 +3,6 @@ import type { QueryCache, QueryClient, QueryKey as QueryKeyType, - ContextOptions, } from '@tanstack/react-query' import { useQueryClient, @@ -40,7 +39,7 @@ import { getQueryStatusLabel, getQueryStatusColor } from './utils' import Explorer from './Explorer' import Logo from './Logo' -export interface DevtoolsOptions extends ContextOptions { +export interface DevtoolsOptions { /** * Set this true if you want the dev tools to default to being open */ @@ -77,9 +76,13 @@ export interface DevtoolsOptions extends ContextOptions { * nonce for style element for CSP */ styleNonce?: string + /** + * Custom instance of QueryClient + */ + queryClient?: QueryClient } -interface DevtoolsPanelOptions extends ContextOptions { +interface DevtoolsPanelOptions { /** * The standard React style object used to style a component with inline styles */ @@ -121,6 +124,10 @@ interface DevtoolsPanelOptions extends ContextOptions { * Use this to add props to the close button. For example, you can add className, style (merge and override default style), onClick (extend default handler), etc. */ closeButtonProps?: React.ComponentPropsWithoutRef<'button'> + /** + * Custom instance of QueryClient + */ + queryClient?: QueryClient } export function ReactQueryDevtools({ @@ -130,7 +137,7 @@ export function ReactQueryDevtools({ toggleButtonProps = {}, position = 'bottom-left', containerElement: Container = 'aside', - context, + queryClient, styleNonce, panelPosition: initialPanelPosition = 'bottom', }: DevtoolsOptions): React.ReactElement | null { @@ -330,7 +337,7 @@ export function ReactQueryDevtools({ ) : null} diff --git a/packages/react-query/src/HydrationBoundary.tsx b/packages/react-query/src/HydrationBoundary.tsx index a1e67673d3..724b528b03 100644 --- a/packages/react-query/src/HydrationBoundary.tsx +++ b/packages/react-query/src/HydrationBoundary.tsx @@ -1,22 +1,23 @@ import * as React from 'react' -import type { HydrateOptions } from '@tanstack/query-core' +import type { HydrateOptions, QueryClient } from '@tanstack/query-core' import { hydrate } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' -import type { ContextOptions } from './types' export interface HydrationBoundaryProps { state?: unknown - options?: HydrateOptions & ContextOptions + options?: HydrateOptions children?: React.ReactNode + queryClient?: QueryClient } export const HydrationBoundary = ({ children, options = {}, state, + queryClient, }: HydrationBoundaryProps) => { - const queryClient = useQueryClient({ context: options.context }) + const client = useQueryClient(queryClient) const optionsRef = React.useRef(options) optionsRef.current = options @@ -27,9 +28,9 @@ export const HydrationBoundary = ({ // hydrate can and should be run *during* render here for SSR to work properly React.useMemo(() => { if (state) { - hydrate(queryClient, state, optionsRef.current) + hydrate(client, state, optionsRef.current) } - }, [queryClient, state]) + }, [client, state]) return children as React.ReactElement } diff --git a/packages/react-query/src/QueryClientProvider.tsx b/packages/react-query/src/QueryClientProvider.tsx index ec94ef003f..11ecf3221a 100644 --- a/packages/react-query/src/QueryClientProvider.tsx +++ b/packages/react-query/src/QueryClientProvider.tsx @@ -1,41 +1,33 @@ import * as React from 'react' import type { QueryClient } from '@tanstack/query-core' -import type { ContextOptions } from './types' -export const defaultContext = React.createContext( +export const QueryClientContext = React.createContext( undefined, ) -function getQueryClientContext( - context: React.Context | undefined, -) { - if (context) { - return context - } - - return defaultContext -} +export const useQueryClient = (queryClient?: QueryClient) => { + const client = React.useContext(QueryClientContext) -export const useQueryClient = ({ context }: ContextOptions = {}) => { - const queryClient = React.useContext(getQueryClientContext(context)) + if (queryClient) { + return queryClient + } - if (!queryClient) { + if (!client) { throw new Error('No QueryClient set, use QueryClientProvider to set one') } - return queryClient + return client } export type QueryClientProviderProps = { client: QueryClient children?: React.ReactNode -} & ContextOptions +} export const QueryClientProvider = ({ client, children, - context, }: QueryClientProviderProps): JSX.Element => { React.useEffect(() => { client.mount() @@ -44,8 +36,6 @@ export const QueryClientProvider = ({ } }, [client]) - const QueryClientContext = getQueryClientContext(context) - return ( {children} diff --git a/packages/react-query/src/__tests__/HydrationBoundary.test.tsx b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx index 617605efbf..0e278d1f74 100644 --- a/packages/react-query/src/__tests__/HydrationBoundary.test.tsx +++ b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx @@ -62,8 +62,6 @@ describe('React hydration', () => { }) test('should hydrate queries to the cache on custom context', async () => { - const context = React.createContext(undefined) - const queryCacheOuter = new QueryCache() const queryCacheInner = new QueryCache() @@ -76,7 +74,6 @@ describe('React hydration', () => { const { data } = useQuery({ queryKey: ['string'], queryFn: () => dataQuery(['string']), - context, }) return (
    @@ -86,9 +83,9 @@ describe('React hydration', () => { } const rendered = render( - + - + diff --git a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx index 71a977acd0..8b1f9ee203 100644 --- a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx @@ -2,13 +2,7 @@ import * as React from 'react' import { render, waitFor } from '@testing-library/react' import { sleep, queryKey, createQueryClient } from './utils' -import { - QueryClient, - QueryClientProvider, - QueryCache, - useQuery, - useQueryClient, -} from '..' +import { QueryClientProvider, QueryCache, useQuery, useQueryClient } from '..' describe('QueryClientProvider', () => { test('sets a specific cache for all queries to use', async () => { @@ -146,69 +140,6 @@ describe('QueryClientProvider', () => { expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) - describe('with custom context', () => { - it('uses the correct context', async () => { - const key = queryKey() - - const contextOuter = React.createContext( - undefined, - ) - const contextInner = React.createContext( - undefined, - ) - - const queryCacheOuter = new QueryCache() - const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) - - const queryCacheInner = new QueryCache() - const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) - - const queryCacheInnerInner = new QueryCache() - const queryClientInnerInner = new QueryClient({ - queryCache: queryCacheInnerInner, - }) - - function Page() { - const { data: testOuter } = useQuery({ - queryKey: key, - queryFn: async () => 'testOuter', - context: contextOuter, - }) - const { data: testInner } = useQuery({ - queryKey: key, - queryFn: async () => 'testInner', - context: contextInner, - }) - const { data: testInnerInner } = useQuery({ - queryKey: key, - queryFn: async () => 'testInnerInner', - }) - - return ( -
    -

    - {testOuter} {testInner} {testInnerInner} -

    -
    - ) - } - - const rendered = render( - - - - - - - , - ) - - await waitFor(() => - rendered.getByText('testOuter testInner testInnerInner'), - ) - }) - }) - describe('useQueryClient', () => { test('should throw an error if no query client has been set', () => { const consoleMock = jest diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx index 2c994dd5db..31937fc3b8 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, waitFor } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import * as React from 'react' import { @@ -1761,4 +1761,27 @@ describe('useInfiniteQuery', () => { expect(cancelFn).toHaveBeenCalled() }) + + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = async () => { + return Promise.resolve('custom client') + } + + function Page() { + const { data } = useInfiniteQuery( + { + queryKey: key, + queryFn, + }, + queryClient, + ) + + return
    data: {data?.pages[0]}
    + } + + const rendered = render() + + await waitFor(() => rendered.getByText('data: custom client')) + }) }) diff --git a/packages/react-query/src/__tests__/useIsFetching.test.tsx b/packages/react-query/src/__tests__/useIsFetching.test.tsx index 54063daa79..a39a2ad775 100644 --- a/packages/react-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/react-query/src/__tests__/useIsFetching.test.tsx @@ -1,6 +1,5 @@ -import { fireEvent, waitFor } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import * as React from 'react' -import { ErrorBoundary } from 'react-error-boundary' import { createQueryClient, @@ -9,7 +8,6 @@ import { setActTimeout, sleep, } from './utils' -import type { QueryClient } from '..' import { QueryCache, useIsFetching, useQuery } from '..' describe('useIsFetching', () => { @@ -174,90 +172,6 @@ describe('useIsFetching', () => { expect(isFetchings).toEqual(expect.not.arrayContaining([2])) }) - describe('with custom context', () => { - it('should update as queries start and stop fetching', async () => { - const context = React.createContext(undefined) - - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - const key = queryKey() - - function Page() { - const [ready, setReady] = React.useState(false) - - const isFetching = useIsFetching(undefined, { context: context }) - - useQuery({ - queryKey: key, - queryFn: async () => { - await sleep(50) - return 'test' - }, - enabled: ready, - context, - }) - - return ( -
    -
    isFetching: {isFetching}
    - -
    - ) - } - - const { findByText, getByRole } = renderWithClient( - queryClient, - , - { - context, - }, - ) - - await findByText('isFetching: 0') - fireEvent.click(getByRole('button', { name: /setReady/i })) - await findByText('isFetching: 1') - await findByText('isFetching: 0') - }) - - it('should throw if the context is not passed to useIsFetching', async () => { - const context = React.createContext(undefined) - - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - const key = queryKey() - - function Page() { - const isFetching = useIsFetching() - - useQuery({ - queryKey: key, - queryFn: async () => 'test', - enabled: true, - context, - throwErrors: true, - }) - - return ( -
    -
    isFetching: {isFetching}
    -
    - ) - } - - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { - context, - }, - ) - - await waitFor(() => rendered.getByText('error boundary')) - }) - }) - it('should show the correct fetching state when mounted after a query', async () => { const queryClient = createQueryClient() const key = queryKey() @@ -285,4 +199,34 @@ describe('useIsFetching', () => { await rendered.findByText('isFetching: 1') await rendered.findByText('isFetching: 0') }) + + it('should use provided custom queryClient', async () => { + const queryClient = createQueryClient() + const key = queryKey() + + function Page() { + useQuery( + { + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, + }, + queryClient, + ) + + const isFetching = useIsFetching({}, queryClient) + + return ( +
    +
    isFetching: {isFetching}
    +
    + ) + } + + const rendered = render() + + await waitFor(() => rendered.getByText('isFetching: 1')) + }) }) diff --git a/packages/react-query/src/__tests__/useIsMutating.test.tsx b/packages/react-query/src/__tests__/useIsMutating.test.tsx index 811cb45ceb..6048eeaf44 100644 --- a/packages/react-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/react-query/src/__tests__/useIsMutating.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, waitFor } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import * as React from 'react' import { useIsMutating } from '../useIsMutating' import { useMutation } from '../useMutation' @@ -8,8 +8,6 @@ import { setActTimeout, sleep, } from './utils' -import { ErrorBoundary } from 'react-error-boundary' -import { QueryClient } from '@tanstack/query-core' import * as MutationCacheModule from '../../../query-core/src/mutationCache' describe('useIsMutating', () => { @@ -196,87 +194,35 @@ describe('useIsMutating', () => { MutationCacheSpy.mockRestore() }) - describe('with custom context', () => { - it('should return the number of fetching mutations', async () => { - const context = React.createContext(undefined) - - const isMutatings: number[] = [] - const queryClient = new QueryClient() - - function IsMutating() { - const isMutating = useIsMutating(undefined, { context }) - isMutatings.push(isMutating) - return null - } + it('should use provided custom queryClient', async () => { + const queryClient = createQueryClient() - function Page() { - const { mutate: mutate1 } = useMutation({ + function Page() { + const isMutating = useIsMutating({}, queryClient) + const { mutate } = useMutation( + { mutationKey: ['mutation1'], mutationFn: async () => { - await sleep(150) - return 'data' - }, - context, - }) - const { mutate: mutate2 } = useMutation({ - mutationKey: ['mutation2'], - mutationFn: async () => { - await sleep(50) + await sleep(10) return 'data' }, - context, - }) - - React.useEffect(() => { - mutate1() - setActTimeout(() => { - mutate2() - }, 50) - }, [mutate1, mutate2]) - - return - } - - renderWithClient(queryClient, , { context }) - await waitFor(() => expect(isMutatings).toEqual([0, 1, 1, 2, 2, 1, 0])) - }) - - it('should throw if the context is not passed to useIsMutating', async () => { - const context = React.createContext(undefined) - - const isMutatings: number[] = [] - const queryClient = new QueryClient() - - function IsMutating() { - const isMutating = useIsMutating(undefined) - isMutatings.push(isMutating) - return null - } - - function Page() { - const { mutate } = useMutation({ - mutationKey: ['mutation'], - mutationFn: async () => 'data', - throwErrors: true, - context, - }) - - React.useEffect(() => { - mutate() - }, [mutate]) + }, + queryClient, + ) - return - } + React.useEffect(() => { + mutate() + }, [mutate]) - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { context }, + return ( +
    +
    mutating: {isMutating}
    +
    ) + } + + const rendered = render() - await waitFor(() => rendered.getByText('error boundary')) - }) + await waitFor(() => rendered.getByText('mutating: 1')) }) }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index fccd24082a..ad57049ee4 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -1,9 +1,8 @@ -import { fireEvent, waitFor } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import '@testing-library/jest-dom' import * as React from 'react' import { ErrorBoundary } from 'react-error-boundary' -import type { QueryClient } from '..' import { MutationCache, QueryCache, useMutation } from '..' import type { UseMutationResult } from '../types' import { @@ -906,73 +905,6 @@ describe('useMutation', () => { expect(onSettledMutate).toHaveBeenCalledTimes(0) }) - describe('with custom context', () => { - it('should be able to reset `data`', async () => { - const context = React.createContext(undefined) - - function Page() { - const { - mutate, - data = 'empty', - reset, - } = useMutation({ - mutationFn: () => Promise.resolve('mutation'), - context, - }) - - return ( -
    -

    {data}

    - - -
    - ) - } - - const { getByRole } = renderWithClient(queryClient, , { context }) - - expect(getByRole('heading').textContent).toBe('empty') - - fireEvent.click(getByRole('button', { name: /mutate/i })) - - await waitFor(() => { - expect(getByRole('heading').textContent).toBe('mutation') - }) - - fireEvent.click(getByRole('button', { name: /reset/i })) - - await waitFor(() => { - expect(getByRole('heading').textContent).toBe('empty') - }) - }) - - it('should throw if the context is not passed to useMutation', async () => { - const context = React.createContext(undefined) - - function Page() { - const { data = '' } = useMutation({ - mutationFn: () => Promise.resolve('mutation'), - }) - - return ( -
    -

    {data}

    -
    - ) - } - - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { context }, - ) - - await waitFor(() => rendered.getByText('error boundary')) - }) - }) - it('should call mutate callbacks only for the last observer', async () => { const onSuccess = jest.fn() const onSuccessMutate = jest.fn() @@ -1138,4 +1070,36 @@ describe('useMutation', () => { expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) }) + + it('should use provided custom queryClient', async () => { + function Page() { + const mutation = useMutation( + { + mutationFn: async (text: string) => { + return Promise.resolve(text) + }, + }, + queryClient, + ) + + return ( +
    + +
    + data: {mutation.data ?? 'null'}, status: {mutation.status} +
    +
    + ) + } + + const rendered = render() + + await rendered.findByText('data: null, status: idle') + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + + await rendered.findByText('data: custom client, status: success') + }) }) diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index f10f22decb..7630454c66 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, waitFor } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import * as React from 'react' import { ErrorBoundary } from 'react-error-boundary' @@ -13,7 +13,6 @@ import { sleep, } from './utils' import type { - QueryClient, QueryFunction, QueryKey, QueryObserverResult, @@ -776,88 +775,6 @@ describe('useQueries', () => { QueriesObserverSpy.mockRestore() }) - describe('with custom context', () => { - it('should return the correct states', async () => { - const context = React.createContext(undefined) - - const key1 = queryKey() - const key2 = queryKey() - const results: UseQueryResult[][] = [] - - function Page() { - const result = useQueries({ - context, - queries: [ - { - queryKey: key1, - queryFn: async () => { - await sleep(5) - return 1 - }, - }, - { - queryKey: key2, - queryFn: async () => { - await sleep(10) - return 2 - }, - }, - ], - }) - results.push(result) - return null - } - - renderWithClient(queryClient, , { context }) - - await sleep(30) - - expect(results[0]).toMatchObject([ - { data: undefined }, - { data: undefined }, - ]) - expect(results[results.length - 1]).toMatchObject([ - { data: 1 }, - { data: 2 }, - ]) - }) - - it('should throw if the context is necessary and is not passed to useQueries', async () => { - const context = React.createContext(undefined) - - const key1 = queryKey() - const key2 = queryKey() - const results: UseQueryResult[][] = [] - - function Page() { - const result = useQueries({ - queries: [ - { - queryKey: key1, - queryFn: async () => 1, - }, - { - queryKey: key2, - queryFn: async () => 2, - }, - ], - }) - results.push(result) - return null - } - - const rendered = renderWithClient( - queryClient, -
    error boundary
    }> - -
    , - { context }, - ) - - await waitFor(() => rendered.getByText('error boundary')) - }) - }) - it("should throw error if in one of queries' queryFn throws and throwErrors is in use", async () => { const key1 = queryKey() const key2 = queryKey() @@ -981,4 +898,29 @@ describe('useQueries', () => { await waitFor(() => rendered.getByText('error boundary')) await waitFor(() => rendered.getByText('single query error')) }) + + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = async () => { + return Promise.resolve('custom client') + } + + function Page() { + const queries = useQueries({ + queries: [ + { + queryKey: key, + queryFn, + }, + ], + queryClient, + }) + + return
    data: {queries[0].data}
    + } + + const rendered = render() + + await waitFor(() => rendered.getByText('data: custom client')) + }) }) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 09ecbad372..2ef6e8bb20 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -1,4 +1,4 @@ -import { act, fireEvent, waitFor } from '@testing-library/react' +import { act, fireEvent, render, waitFor } from '@testing-library/react' import '@testing-library/jest-dom' import * as React from 'react' import { @@ -6057,4 +6057,27 @@ describe('useQuery', () => { fireEvent.click(fetchBtn) await waitFor(() => rendered.getByText('data: 3')) }) + + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = async () => { + return Promise.resolve('custom client') + } + + function Page() { + const { data } = useQuery( + { + queryKey: key, + queryFn, + }, + queryClient, + ) + + return
    data: {data}
    + } + + const rendered = render() + + await waitFor(() => rendered.getByText('data: custom client')) + }) }) diff --git a/packages/react-query/src/__tests__/utils.tsx b/packages/react-query/src/__tests__/utils.tsx index d5502a3e29..a45777f790 100644 --- a/packages/react-query/src/__tests__/utils.tsx +++ b/packages/react-query/src/__tests__/utils.tsx @@ -1,26 +1,21 @@ import * as React from 'react' import { act, render } from '@testing-library/react' -import type { ContextOptions, QueryClientConfig, MutationOptions } from '..' +import type { QueryClientConfig, MutationOptions } from '..' import { QueryClient, QueryClientProvider } from '..' import * as utils from '@tanstack/query-core' export function renderWithClient( client: QueryClient, ui: React.ReactElement, - options: ContextOptions = {}, ): ReturnType { const { rerender, ...result } = render( - - {ui} - , + {ui}, ) return { ...result, rerender: (rerenderUi: React.ReactElement) => rerender( - - {rerenderUi} - , + {rerenderUi}, ), } as any } diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index ea69b6cd2e..4b0f484a9f 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -9,7 +9,7 @@ export { useQueries } from './useQueries' export type { QueriesResults, QueriesOptions } from './useQueries' export { useQuery } from './useQuery' export { - defaultContext, + QueryClientContext, QueryClientProvider, useQueryClient, } from './QueryClientProvider' diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index b98f6e5f92..163924cde9 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -1,6 +1,5 @@ /* istanbul ignore file */ -import type * as React from 'react' import type { InfiniteQueryObserverOptions, InfiniteQueryObserverResult, @@ -13,14 +12,6 @@ import type { DefinedQueryObserverResult, WithRequired, } from '@tanstack/query-core' -import type { QueryClient } from '@tanstack/query-core' - -export interface ContextOptions { - /** - * Use this to pass your React Query context. Otherwise, `defaultContext` will be used. - */ - context?: React.Context -} export interface UseBaseQueryOptions< TQueryFnData = unknown, @@ -28,11 +19,10 @@ export interface UseBaseQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends ContextOptions, - WithRequired< - QueryObserverOptions, - 'queryKey' - > {} +> extends WithRequired< + QueryObserverOptions, + 'queryKey' + > {} export interface UseQueryOptions< TQueryFnData = unknown, @@ -50,17 +40,16 @@ export interface UseInfiniteQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends ContextOptions, - WithRequired< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >, - 'queryKey' - > {} +> extends WithRequired< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + 'queryKey' + > {} export type UseBaseQueryResult< TData = unknown, @@ -92,11 +81,10 @@ export interface UseMutationOptions< TError = Error, TVariables = void, TContext = unknown, -> extends ContextOptions, - Omit< - MutationObserverOptions, - '_defaulted' | 'variables' - > {} +> extends Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} export type UseMutateFunction< TData = unknown, diff --git a/packages/react-query/src/useBaseQuery.ts b/packages/react-query/src/useBaseQuery.ts index 449898056d..ed0e57be96 100644 --- a/packages/react-query/src/useBaseQuery.ts +++ b/packages/react-query/src/useBaseQuery.ts @@ -1,6 +1,6 @@ import * as React from 'react' -import type { QueryKey, QueryObserver } from '@tanstack/query-core' +import type { QueryClient, QueryKey, QueryObserver } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary' import { useQueryClient } from './QueryClientProvider' @@ -28,11 +28,12 @@ export function useBaseQuery< TQueryKey >, Observer: typeof QueryObserver, + queryClient?: QueryClient, ) { - const queryClient = useQueryClient({ context: options.context }) + const client = useQueryClient(queryClient) const isRestoring = useIsRestoring() const errorResetBoundary = useQueryErrorResetBoundary() - const defaultedOptions = queryClient.defaultQueryOptions(options) + const defaultedOptions = client.defaultQueryOptions(options) // Make sure results are optimistically set in fetching state before subscribing or updating options defaultedOptions._optimisticResults = isRestoring @@ -66,7 +67,7 @@ export function useBaseQuery< const [observer] = React.useState( () => new Observer( - queryClient, + client, defaultedOptions, ), ) diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 532ba03e08..f0ab16e2de 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -1,4 +1,4 @@ -import type { QueryObserver, QueryKey } from '@tanstack/query-core' +import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' import { useBaseQuery } from './useBaseQuery' @@ -17,9 +17,11 @@ export function useInfiniteQuery< TQueryFnData, TQueryKey >, + queryClient?: QueryClient, ): UseInfiniteQueryResult { return useBaseQuery( options, InfiniteQueryObserver as typeof QueryObserver, + queryClient, ) as UseInfiniteQueryResult } diff --git a/packages/react-query/src/useIsFetching.ts b/packages/react-query/src/useIsFetching.ts index 08917a20a5..1e67425365 100644 --- a/packages/react-query/src/useIsFetching.ts +++ b/packages/react-query/src/useIsFetching.ts @@ -1,18 +1,15 @@ import * as React from 'react' -import type { QueryFilters } from '@tanstack/query-core' +import type { QueryClient, QueryFilters } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' -import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' -interface Options extends ContextOptions {} - export function useIsFetching( filters?: QueryFilters, - options: Options = {}, + queryClient?: QueryClient, ): number { - const queryClient = useQueryClient({ context: options.context }) - const queryCache = queryClient.getQueryCache() + const client = useQueryClient(queryClient) + const queryCache = client.getQueryCache() return React.useSyncExternalStore( React.useCallback( @@ -20,7 +17,7 @@ export function useIsFetching( queryCache.subscribe(notifyManager.batchCalls(onStoreChange)), [queryCache], ), - () => queryClient.isFetching(filters), - () => queryClient.isFetching(filters), + () => client.isFetching(filters), + () => client.isFetching(filters), ) } diff --git a/packages/react-query/src/useIsMutating.ts b/packages/react-query/src/useIsMutating.ts index 3c70eba3cf..d4624d7d37 100644 --- a/packages/react-query/src/useIsMutating.ts +++ b/packages/react-query/src/useIsMutating.ts @@ -1,18 +1,15 @@ import * as React from 'react' -import type { MutationFilters } from '@tanstack/query-core' +import type { MutationFilters, QueryClient } from '@tanstack/query-core' import { notifyManager } from '@tanstack/query-core' -import type { ContextOptions } from './types' import { useQueryClient } from './QueryClientProvider' -interface Options extends ContextOptions {} - export function useIsMutating( filters?: MutationFilters, - options: Options = {}, + queryClient?: QueryClient, ): number { - const queryClient = useQueryClient({ context: options.context }) - const mutationCache = queryClient.getMutationCache() + const client = useQueryClient(queryClient) + const mutationCache = client.getMutationCache() return React.useSyncExternalStore( React.useCallback( @@ -20,7 +17,7 @@ export function useIsMutating( mutationCache.subscribe(notifyManager.batchCalls(onStoreChange)), [mutationCache], ), - () => queryClient.isMutating(filters), - () => queryClient.isMutating(filters), + () => client.isMutating(filters), + () => client.isMutating(filters), ) } diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index 538acc7ef9..c6621ccba7 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -1,4 +1,5 @@ import * as React from 'react' +import type { QueryClient } from '@tanstack/query-core' import { notifyManager, MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { @@ -17,13 +18,14 @@ export function useMutation< TContext = unknown, >( options: UseMutationOptions, + queryClient?: QueryClient, ): UseMutationResult { - const queryClient = useQueryClient({ context: options.context }) + const client = useQueryClient(queryClient) const [observer] = React.useState( () => new MutationObserver( - queryClient, + client, options, ), ) diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index 6e39248a02..be34518f02 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -4,6 +4,7 @@ import type { QueryKey, QueryFunction, QueriesPlaceholderDataFunction, + QueryClient, } from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' @@ -23,7 +24,7 @@ import { } from './suspense' // This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. -// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +// `placeholderData` function does not have a parameter type UseQueryOptionsForUseQueries< TQueryFnData = unknown, TError = Error, @@ -31,7 +32,7 @@ type UseQueryOptionsForUseQueries< TQueryKey extends QueryKey = QueryKey, > = Omit< UseQueryOptions, - 'context' | 'placeholderData' + 'placeholderData' > & { placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction } @@ -155,18 +156,18 @@ export type QueriesResults< export function useQueries({ queries, - context, + queryClient, }: { queries: readonly [...QueriesOptions] - context?: UseQueryOptions['context'] + queryClient?: QueryClient }): QueriesResults { - const queryClient = useQueryClient({ context }) + const client = useQueryClient(queryClient) const isRestoring = useIsRestoring() const defaultedQueries = React.useMemo( () => queries.map((options) => { - const defaultedOptions = queryClient.defaultQueryOptions(options) + const defaultedOptions = client.defaultQueryOptions(options) // Make sure the results are already in fetching state before subscribing or updating options defaultedOptions._optimisticResults = isRestoring @@ -175,11 +176,11 @@ export function useQueries({ return defaultedOptions }), - [queries, queryClient, isRestoring], + [queries, client, isRestoring], ) const [observer] = React.useState( - () => new QueriesObserver(queryClient, defaultedQueries), + () => new QueriesObserver(client, defaultedQueries), ) const optimisticResult = observer.getOptimisticResult(defaultedQueries) diff --git a/packages/react-query/src/useQuery.ts b/packages/react-query/src/useQuery.ts index a641351234..df49edee9d 100644 --- a/packages/react-query/src/useQuery.ts +++ b/packages/react-query/src/useQuery.ts @@ -1,4 +1,4 @@ -import type { QueryKey } from '@tanstack/query-core' +import type { QueryClient, QueryKey } from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' import type { DefinedUseQueryResult, @@ -33,6 +33,7 @@ export function useQuery< TQueryKey extends QueryKey = QueryKey, >( options: UndefinedInitialDataOptions, + queryClient?: QueryClient, ): UseQueryResult export function useQuery< @@ -42,6 +43,7 @@ export function useQuery< TQueryKey extends QueryKey = QueryKey, >( options: DefinedInitialDataOptions, + queryClient?: QueryClient, ): DefinedUseQueryResult export function useQuery< @@ -49,6 +51,9 @@ export function useQuery< TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->(options: UseQueryOptions) { - return useBaseQuery(options, QueryObserver) +>( + options: UseQueryOptions, + queryClient?: QueryClient, +) { + return useBaseQuery(options, QueryObserver, queryClient) } diff --git a/packages/solid-query/src/QueryClientProvider.tsx b/packages/solid-query/src/QueryClientProvider.tsx index 8063d7cd7d..abc766d843 100644 --- a/packages/solid-query/src/QueryClientProvider.tsx +++ b/packages/solid-query/src/QueryClientProvider.tsx @@ -1,34 +1,29 @@ import type { QueryClient } from '@tanstack/query-core' -import type { Context, JSX } from 'solid-js' +import type { JSX } from 'solid-js' import { createContext, useContext, onMount, onCleanup } from 'solid-js' -import type { ContextOptions } from './types' -export const defaultContext = createContext(undefined) +export const QueryClientContext = createContext( + undefined, +) -function getQueryClientContext( - context: Context | undefined, -) { - if (context) { - return context - } - - return defaultContext -} +export const useQueryClient = (queryClient?: QueryClient) => { + const client = useContext(QueryClientContext) -export const useQueryClient = ({ context }: ContextOptions = {}) => { - const queryClient = useContext(getQueryClientContext(context)) + if (queryClient) { + return queryClient + } - if (!queryClient) { + if (!client) { throw new Error('No QueryClient set, use QueryClientProvider to set one') } - return queryClient + return client } export type QueryClientProviderProps = { client: QueryClient children?: JSX.Element -} & ContextOptions +} export const QueryClientProvider = ( props: QueryClientProviderProps, @@ -38,8 +33,6 @@ export const QueryClientProvider = ( }) onCleanup(() => props.client.unmount()) - const QueryClientContext = getQueryClientContext(props.context) - return ( {props.children} diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index a6cd97a235..c4018544e0 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -1,8 +1,7 @@ import { render, screen, waitFor } from 'solid-testing-library' import { queryKey } from './utils' -import { QueryCache, QueryClient } from '@tanstack/query-core' -import { createContext } from 'solid-js' +import { QueryCache } from '@tanstack/query-core' import { createQuery, QueryClientProvider, useQueryClient } from '..' import { createQueryClient, sleep } from './utils' @@ -144,65 +143,6 @@ describe('QueryClientProvider', () => { expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) - describe('with custom context', () => { - it('uses the correct context', async () => { - const key = queryKey() - - const contextOuter = createContext(undefined) - const contextInner = createContext(undefined) - - const queryCacheOuter = new QueryCache() - const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) - - const queryCacheInner = new QueryCache() - const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) - - const queryCacheInnerInner = new QueryCache() - const queryClientInnerInner = new QueryClient({ - queryCache: queryCacheInnerInner, - }) - - function Page() { - const queryOuter = createQuery(() => ({ - queryKey: key, - queryFn: async () => 'testOuter', - context: contextOuter, - })) - const queryInner = createQuery(() => ({ - queryKey: key, - queryFn: async () => 'testInner', - context: contextInner, - })) - const queryInnerInner = createQuery(() => ({ - queryKey: key, - queryFn: async () => 'testInnerInner', - })) - - return ( -
    -

    - {queryOuter.data} {queryInner.data} {queryInnerInner.data} -

    -
    - ) - } - - render(() => ( - - - - - - - - )) - - await waitFor(() => - screen.getByText('testOuter testInner testInnerInner'), - ) - }) - }) - describe('useQueryClient', () => { it('should throw an error if no query client has been set', () => { const consoleMock = jest diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx index ee88ae121d..f1d6c90300 100644 --- a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -1877,4 +1877,27 @@ describe('useInfiniteQuery', () => { expect(cancelFn).toHaveBeenCalled() }) + + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = () => { + return Promise.resolve('custom client') + } + + function Page() { + const state = createInfiniteQuery( + () => ({ queryKey: key, queryFn }), + () => queryClient, + ) + return ( +
    +

    Status: {state.data?.pages[0]}

    +
    + ) + } + + render(() => ) + + await waitFor(() => screen.getByText('Status: custom client')) + }) }) diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index 734176a080..1dab157bfa 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -1,13 +1,11 @@ import '@testing-library/jest-dom' import { - createContext, createEffect, createRenderEffect, createSignal, ErrorBoundary, } from 'solid-js' import { fireEvent, render, screen, waitFor } from 'solid-testing-library' -import type { QueryClient } from '..' import { createMutation, MutationCache, @@ -1005,74 +1003,6 @@ describe('useMutation', () => { expect(onSettledMutate).toHaveBeenCalledTimes(0) }) - describe('with custom context', () => { - it('should be able to reset `data`', async () => { - const context = createContext(undefined) - - function Page() { - const mutation = createMutation(() => ({ - mutationFn: () => Promise.resolve('mutation'), - context, - })) - - return ( -
    -

    {mutation.data ?? 'empty'}

    - - -
    - ) - } - - render(() => ( - - - - )) - - expect(screen.getByRole('heading').textContent).toBe('empty') - - fireEvent.click(screen.getByRole('button', { name: /mutate/i })) - - await waitFor(() => { - expect(screen.getByRole('heading').textContent).toBe('mutation') - }) - - fireEvent.click(screen.getByRole('button', { name: /reset/i })) - - await waitFor(() => { - expect(screen.getByRole('heading').textContent).toBe('empty') - }) - }) - - it('should throw if the context is not passed to useMutation', async () => { - const context = createContext(undefined) - - function Page() { - const { data = '' } = createMutation(() => ({ - mutationFn: () => Promise.resolve('mutation'), - })) - - return ( -
    -

    {data}

    -
    - ) - } - - render(() => ( - -
    error boundary
    }> - -
    - , -
    - )) - - await waitFor(() => screen.getByText('error boundary')) - }) - }) - it('should call mutate callbacks only for the last observer', async () => { const onSuccess = jest.fn() const onSuccessMutate = jest.fn() @@ -1254,4 +1184,36 @@ describe('useMutation', () => { expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) }) + + it('should use provided custom queryClient', async () => { + function Page() { + const mutation = createMutation( + () => ({ + mutationFn: async (text: string) => { + return Promise.resolve(text) + }, + }), + () => queryClient, + ) + + return ( +
    + +
    + data: {mutation.data ?? 'null'}, status: {mutation.status} +
    +
    + ) + } + + render(() => ) + + await screen.findByText('data: null, status: idle') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await screen.findByText('data: custom client, status: success') + }) }) diff --git a/packages/solid-query/src/__tests__/createQueries.test.tsx b/packages/solid-query/src/__tests__/createQueries.test.tsx index 490ca37534..676b8559b7 100644 --- a/packages/solid-query/src/__tests__/createQueries.test.tsx +++ b/packages/solid-query/src/__tests__/createQueries.test.tsx @@ -3,15 +3,9 @@ import { fireEvent, render, screen, waitFor } from 'solid-testing-library' import * as QueriesObserverModule from '../../../query-core/src/queriesObserver' import type { QueryFunctionContext, QueryKey } from '@tanstack/query-core' -import { - createContext, - createRenderEffect, - createSignal, - ErrorBoundary, -} from 'solid-js' +import { createRenderEffect, createSignal } from 'solid-js' import type { CreateQueryResult, - QueryClient, QueryFunction, QueryObserverResult, SolidQueryOptions, @@ -796,91 +790,26 @@ describe('useQueries', () => { QueriesObserverSpy.mockRestore() }) - describe('with custom context', () => { - it('should return the correct states', async () => { - const context = createContext(undefined) - - const key1 = queryKey() - const key2 = queryKey() - const results: CreateQueryResult[][] = [] - - function Page() { - const result = createQueries(() => ({ - context, - queries: [ - { - queryKey: key1, - queryFn: async () => { - await sleep(5) - return 1 - }, - }, - { - queryKey: key2, - queryFn: async () => { - await sleep(10) - return 2 - }, - }, - ], - })) - createRenderEffect(() => { - results.push([...result]) - }) - return null - } + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = () => { + return Promise.resolve('custom client') + } - render(() => ( - - - - )) - - await sleep(30) - - expect(results[0]).toMatchObject([ - { data: undefined }, - { data: undefined }, - ]) - expect(results[results.length - 1]).toMatchObject([ - { data: 1 }, - { data: 2 }, - ]) - }) - - it('should throw if the context is necessary and is not passed to useQueries', async () => { - const context = createContext(undefined) - - const key1 = queryKey() - const key2 = queryKey() - const results: CreateQueryResult[][] = [] - - function Page() { - const result = createQueries(() => ({ - queries: [ - { - queryKey: key1, - queryFn: async () => 1, - }, - { - queryKey: key2, - queryFn: async () => 2, - }, - ], - })) - results.push(result) - return null - } + function Page() { + const state = createQueries(() => ({ + queries: [{ queryKey: key, queryFn }], + queryClient, + })) + return ( +
    +

    Status: {state[0]?.data}

    +
    + ) + } - render(() => ( - -
    error boundary
    }> - -
    -
    - )) + render(() => ) - await waitFor(() => screen.getByText('error boundary')) - }) + await waitFor(() => screen.getByText('Status: custom client')) }) }) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index c1d1400619..a733828f4c 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -6155,4 +6155,27 @@ describe('createQuery', () => { fireEvent.click(fetchBtn) await waitFor(() => screen.getByText('data: 3')) }) + + it('should use provided custom queryClient', async () => { + const key = queryKey() + const queryFn = () => { + return Promise.resolve('custom client') + } + + function Page() { + const state = createQuery( + () => ({ queryKey: key, queryFn }), + () => queryClient, + ) + return ( +
    +

    Status: {state.data}

    +
    + ) + } + + render(() => ) + + await waitFor(() => screen.getByText('Status: custom client')) + }) }) diff --git a/packages/solid-query/src/__tests__/useIsFetching.test.tsx b/packages/solid-query/src/__tests__/useIsFetching.test.tsx index 6066655de6..5ef1c33623 100644 --- a/packages/solid-query/src/__tests__/useIsFetching.test.tsx +++ b/packages/solid-query/src/__tests__/useIsFetching.test.tsx @@ -1,14 +1,6 @@ import { fireEvent, render, screen, waitFor } from 'solid-testing-library' -import { - createContext, - createEffect, - createRenderEffect, - createSignal, - ErrorBoundary, - Show, -} from 'solid-js' -import type { QueryClient } from '..' +import { createEffect, createRenderEffect, createSignal, Show } from 'solid-js' import { createQuery, QueryCache, QueryClientProvider, useIsFetching } from '..' import { createQueryClient, queryKey, setActTimeout, sleep } from './utils' @@ -197,90 +189,6 @@ describe('useIsFetching', () => { expect(isFetchings).toEqual(expect.not.arrayContaining([2])) }) - describe('with custom context', () => { - it('should update as queries start and stop fetching', async () => { - const context = createContext(undefined) - - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - const key = queryKey() - - function Page() { - const [ready, setReady] = createSignal(false) - - const isFetching = useIsFetching(() => ({ - options: { - context, - }, - })) - - createQuery(() => ({ - queryKey: key, - queryFn: async () => { - await sleep(50) - return 'test' - }, - enabled: ready(), - context, - })) - - return ( -
    -
    isFetching: {isFetching}
    - -
    - ) - } - - render(() => ( - - - - )) - - await screen.findByText('isFetching: 0') - fireEvent.click(screen.getByRole('button', { name: /setReady/i })) - await screen.findByText('isFetching: 1') - await screen.findByText('isFetching: 0') - }) - - it('should throw if the context is not passed to useIsFetching', async () => { - const context = createContext(undefined) - - const queryCache = new QueryCache() - const queryClient = createQueryClient({ queryCache }) - const key = queryKey() - - function Page() { - const isFetching = useIsFetching() - - createQuery(() => ({ - queryKey: key, - queryFn: async () => 'test', - enabled: true, - context, - throwErrors: true, - })) - - return ( -
    -
    isFetching: {isFetching}
    -
    - ) - } - - render(() => ( - -
    error boundary
    }> - -
    -
    - )) - - await waitFor(() => screen.getByText('error boundary')) - }) - }) - it('should show the correct fetching state when mounted after a query', async () => { const queryClient = createQueryClient() const key = queryKey() @@ -312,4 +220,34 @@ describe('useIsFetching', () => { await screen.findByText('isFetching: 1') await screen.findByText('isFetching: 0') }) + + it('should use provided custom queryClient', async () => { + const queryClient = createQueryClient() + const key = queryKey() + + function Page() { + createQuery( + () => ({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'test' + }, + }), + () => queryClient, + ) + + const isFetching = useIsFetching(() => ({ queryClient })) + + return ( +
    +
    isFetching: {isFetching}
    +
    + ) + } + + render(() => ) + + await screen.findByText('isFetching: 1') + }) }) diff --git a/packages/solid-query/src/__tests__/useIsMutating.test.tsx b/packages/solid-query/src/__tests__/useIsMutating.test.tsx index 642392774e..5aa294e68e 100644 --- a/packages/solid-query/src/__tests__/useIsMutating.test.tsx +++ b/packages/solid-query/src/__tests__/useIsMutating.test.tsx @@ -1,20 +1,8 @@ import { fireEvent, screen, waitFor } from 'solid-testing-library' -import { - createMutation, - QueryClient, - QueryClientProvider, - useIsMutating, -} from '..' +import { createMutation, QueryClientProvider, useIsMutating } from '..' import { createQueryClient, sleep } from './utils' -import { - createContext, - createEffect, - createRenderEffect, - createSignal, - ErrorBoundary, - Show, -} from 'solid-js' +import { createEffect, createRenderEffect, createSignal, Show } from 'solid-js' import { render } from 'solid-testing-library' import * as MutationCacheModule from '../../../query-core/src/mutationCache' import { setActTimeout } from './utils' @@ -234,99 +222,35 @@ describe('useIsMutating', () => { MutationCacheSpy.mockRestore() }) - describe('with custom context', () => { - it('should return the number of fetching mutations', async () => { - const context = createContext(undefined) - - const isMutatings: number[] = [] - const queryClient = new QueryClient() - - function IsMutating() { - const isMutating = useIsMutating(() => ({ - options: { - context, - }, - })) - - createRenderEffect(() => { - isMutatings.push(isMutating()) - }) - - return null - } + it('should use provided custom queryClient', async () => { + const queryClient = createQueryClient() - function Page() { - const { mutate: mutate1 } = createMutation(() => ({ + function Page() { + const isMutating = useIsMutating(() => ({ queryClient })) + const { mutate } = createMutation( + () => ({ mutationKey: ['mutation1'], mutationFn: async () => { - await sleep(150) + await sleep(10) return 'data' }, - context, - })) - const { mutate: mutate2 } = createMutation(() => ({ - mutationKey: ['mutation2'], - mutationFn: async () => { - await sleep(50) - return 'data' - }, - context, - })) - - createEffect(() => { - mutate1() - setActTimeout(() => { - mutate2() - }, 50) - }) - - return - } - - render(() => ( - - - - )) - await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0])) - }) - - it('should throw if the context is not passed to useIsMutating', async () => { - const context = createContext(undefined) - - const isMutatings: number[] = [] - const queryClient = new QueryClient() - - function IsMutating() { - const isMutating = useIsMutating(undefined) - isMutatings.push(isMutating()) - return null - } - - function Page() { - const { mutate } = createMutation(() => ({ - mutationKey: ['mutation'], - mutationFn: async () => 'data', - throwErrors: true, - context, - })) + }), + () => queryClient, + ) - createEffect(() => { - mutate() - }) + createEffect(() => { + mutate() + }) - return - } + return ( +
    +
    mutating: {isMutating}
    +
    + ) + } - render(() => ( - -
    error boundary
    }> - -
    -
    - )) + render(() => ) - await waitFor(() => screen.getByText('error boundary')) - }) + await waitFor(() => screen.findByText('mutating: 1')) }) }) diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts index 32c2b03c21..d9143badf8 100644 --- a/packages/solid-query/src/createBaseQuery.ts +++ b/packages/solid-query/src/createBaseQuery.ts @@ -1,4 +1,5 @@ import type { + QueryClient, QueryKey, QueryObserver, QueryObserverResult, @@ -31,15 +32,14 @@ export function createBaseQuery< CreateBaseQueryOptions >, Observer: typeof QueryObserver, + queryClient?: () => QueryClient, ) { - const queryClient = createMemo(() => - useQueryClient({ context: options().context }), - ) + const client = createMemo(() => useQueryClient(queryClient?.())) const emptyData = Symbol('empty') - const defaultedOptions = queryClient().defaultQueryOptions(options()) + const defaultedOptions = client().defaultQueryOptions(options()) defaultedOptions._optimisticResults = 'optimistic' - const observer = new Observer(queryClient(), defaultedOptions) + const observer = new Observer(client(), defaultedOptions) const [state, setState] = createStore>( observer.getOptimisticResult(defaultedOptions), @@ -85,7 +85,7 @@ export function createBaseQuery< }) createComputed(() => { - observer.setOptions(queryClient().defaultQueryOptions(options())) + observer.setOptions(client().defaultQueryOptions(options())) }) createComputed( diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts index a6e91f5e61..01b2e57cd3 100644 --- a/packages/solid-query/src/createInfiniteQuery.ts +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -1,4 +1,4 @@ -import type { QueryObserver, QueryKey } from '@tanstack/query-core' +import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, @@ -14,9 +14,11 @@ export function createInfiniteQuery< TQueryKey extends QueryKey = QueryKey, >( options: CreateInfiniteQueryOptions, + queryClient?: () => QueryClient, ): CreateInfiniteQueryResult { return createBaseQuery( createMemo(() => options()), InfiniteQueryObserver as typeof QueryObserver, + queryClient, ) as CreateInfiniteQueryResult } diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index 83b9935376..ca19c19287 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -1,3 +1,4 @@ +import type { QueryClient } from '@tanstack/query-core' import { MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { @@ -17,11 +18,12 @@ export function createMutation< TContext = unknown, >( options: CreateMutationOptions, + queryClient?: () => QueryClient, ): CreateMutationResult { - const queryClient = useQueryClient({ context: options().context }) + const client = useQueryClient(queryClient?.()) const observer = new MutationObserver( - queryClient, + client, options(), ) diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index bb54964039..410b6d15a1 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -1,5 +1,6 @@ import type { QueriesPlaceholderDataFunction, + QueryClient, QueryFunction, QueryKey, } from '@tanstack/query-core' @@ -10,7 +11,7 @@ import { useQueryClient } from './QueryClientProvider' import type { CreateQueryResult, SolidQueryOptions } from './types' // This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. -// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +// `placeholderData` function does not have a parameter type CreateQueryOptionsForCreateQueries< TQueryFnData = unknown, TError = Error, @@ -18,7 +19,7 @@ type CreateQueryOptionsForCreateQueries< TQueryKey extends QueryKey = QueryKey, > = Omit< SolidQueryOptions, - 'context' | 'placeholderData' + 'placeholderData' > & { placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction } @@ -148,10 +149,10 @@ export type QueriesResults< export function createQueries( queriesOptions: () => { queries: readonly [...QueriesOptions] - context?: SolidQueryOptions['context'] + queryClient?: QueryClient }, ): QueriesResults { - const queryClient = useQueryClient({ context: queriesOptions().context }) + const queryClient = useQueryClient(queriesOptions().queryClient) const defaultedQueries = queriesOptions().queries.map((options) => { const defaultedOptions = queryClient.defaultQueryOptions(options) diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts index d6a0c69538..9a00aba475 100644 --- a/packages/solid-query/src/createQuery.ts +++ b/packages/solid-query/src/createQuery.ts @@ -1,4 +1,4 @@ -import type { QueryKey } from '@tanstack/query-core' +import type { QueryClient, QueryKey } from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' import { createMemo } from 'solid-js' import { createBaseQuery } from './createBaseQuery' @@ -39,6 +39,7 @@ export function createQuery< TQueryKey extends QueryKey = QueryKey, >( options: UndefinedInitialDataOptions, + queryClient?: () => QueryClient, ): CreateQueryResult export function createQuery< @@ -48,15 +49,20 @@ export function createQuery< TQueryKey extends QueryKey = QueryKey, >( options: DefinedInitialDataOptions, + queryClient?: () => QueryClient, ): DefinedCreateQueryResult export function createQuery< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->(options: CreateQueryOptions) { +>( + options: CreateQueryOptions, + queryClient?: () => QueryClient, +) { return createBaseQuery( createMemo(() => options()), QueryObserver, + queryClient, ) } diff --git a/packages/solid-query/src/index.ts b/packages/solid-query/src/index.ts index 56ba952485..633ceb72d5 100644 --- a/packages/solid-query/src/index.ts +++ b/packages/solid-query/src/index.ts @@ -10,7 +10,7 @@ export * from '@tanstack/query-core' export * from './types' export { createQuery } from './createQuery' export { - defaultContext, + QueryClientContext, QueryClientProvider, useQueryClient, } from './QueryClientProvider' diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index 4613945acc..f803dff443 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -1,8 +1,6 @@ /* istanbul ignore file */ -import type { Context } from 'solid-js' import type { - QueryClient, QueryKey, QueryObserverOptions, QueryObserverResult, @@ -15,13 +13,6 @@ import type { WithRequired, } from '@tanstack/query-core' -export interface ContextOptions { - /** - * Use this to pass your Solid Query context. Otherwise, `defaultContext` will be used. - */ - context?: Context -} - export type FunctionedParams = () => T export interface CreateBaseQueryOptions< @@ -30,11 +21,10 @@ export interface CreateBaseQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends ContextOptions, - WithRequired< - QueryObserverOptions, - 'queryKey' - > {} +> extends WithRequired< + QueryObserverOptions, + 'queryKey' + > {} export interface SolidQueryOptions< TQueryFnData = unknown, @@ -88,17 +78,16 @@ export interface SolidInfiniteQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends ContextOptions, - Omit< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >, - 'queryKey' - > { +> extends Omit< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + 'queryKey' + > { queryKey: TQueryKey } @@ -128,11 +117,10 @@ export interface SolidMutationOptions< TError = Error, TVariables = void, TContext = unknown, -> extends ContextOptions, - Omit< - MutationObserverOptions, - '_defaulted' | 'variables' - > {} +> extends Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} export type CreateMutationOptions< TData = unknown, diff --git a/packages/solid-query/src/useIsFetching.ts b/packages/solid-query/src/useIsFetching.ts index 3e069496d2..0482471615 100644 --- a/packages/solid-query/src/useIsFetching.ts +++ b/packages/solid-query/src/useIsFetching.ts @@ -1,18 +1,15 @@ -import type { QueryFilters } from '@tanstack/query-core' +import type { QueryClient, QueryFilters } from '@tanstack/query-core' import type { Accessor } from 'solid-js' import { createMemo, createSignal, onCleanup } from 'solid-js' import { useQueryClient } from './QueryClientProvider' -import type { ContextOptions } from './types' type Options = () => { filters?: QueryFilters - options?: ContextOptions + queryClient?: QueryClient } export function useIsFetching(options: Options = () => ({})): Accessor { - const queryClient = createMemo(() => - useQueryClient({ context: options().options?.context }), - ) + const queryClient = createMemo(() => useQueryClient(options().queryClient)) const queryCache = createMemo(() => queryClient().getQueryCache()) const [fetches, setFetches] = createSignal( diff --git a/packages/solid-query/src/useIsMutating.ts b/packages/solid-query/src/useIsMutating.ts index 5ef921b79f..420cbbb88c 100644 --- a/packages/solid-query/src/useIsMutating.ts +++ b/packages/solid-query/src/useIsMutating.ts @@ -1,18 +1,15 @@ -import type { MutationFilters } from '@tanstack/query-core' -import type { ContextOptions } from './types' +import type { MutationFilters, QueryClient } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { Accessor } from 'solid-js' import { createSignal, onCleanup, createMemo } from 'solid-js' type Options = () => { filters?: MutationFilters - options?: ContextOptions + queryClient?: QueryClient } export function useIsMutating(options: Options = () => ({})): Accessor { - const queryClient = createMemo(() => - useQueryClient({ context: options().options?.context }), - ) + const queryClient = createMemo(() => useQueryClient(options().queryClient)) const mutationCache = createMemo(() => queryClient().getMutationCache()) const [mutations, setMutations] = createSignal( diff --git a/packages/vue-query/src/__tests__/useIsMutating.test.ts b/packages/vue-query/src/__tests__/useIsMutating.test.ts index 8aff85ff57..21be6fbc81 100644 --- a/packages/vue-query/src/__tests__/useIsMutating.test.ts +++ b/packages/vue-query/src/__tests__/useIsMutating.test.ts @@ -3,7 +3,6 @@ import { onScopeDispose, reactive, ref } from 'vue-demi' import { flushPromises, successMutator } from './test-utils' import { useMutation } from '../useMutation' import { unrefFilterArgs, useIsMutating } from '../useIsMutating' -import { useQueryClient } from '../useQueryClient' jest.mock('../useQueryClient') @@ -61,13 +60,6 @@ describe('useIsMutating', () => { onScopeDisposeMock.mockReset() }) - test('should call `useQueryClient` with a proper `queryClientKey`', async () => { - const queryClientKey = 'foo' - useIsMutating({ queryClientKey }) - - expect(useQueryClient).toHaveBeenCalledWith(queryClientKey) - }) - test('should properly update filters', async () => { const filter = reactive({ mutationKey: ['foo'] }) const { mutate } = useMutation({ diff --git a/packages/vue-query/src/__tests__/useQueries.test.ts b/packages/vue-query/src/__tests__/useQueries.test.ts index 2ad58f3558..14ae31308c 100644 --- a/packages/vue-query/src/__tests__/useQueries.test.ts +++ b/packages/vue-query/src/__tests__/useQueries.test.ts @@ -211,24 +211,4 @@ describe('useQueries', () => { expect(useQueryClient).toHaveBeenCalledTimes(0) }) - - test('should use queryClient provided via query options', async () => { - const queryClient = new QueryClient() - const queries = [ - { - queryKey: ['key41'], - queryFn: simpleFetcher, - queryClient, - }, - { - queryKey: ['key42'], - queryFn: simpleFetcher, - }, - ] - - useQueries({ queries }) - await flushPromises() - - expect(useQueryClient).toHaveBeenCalledTimes(0) - }) }) diff --git a/packages/vue-query/src/__tests__/useQuery.test.ts b/packages/vue-query/src/__tests__/useQuery.test.ts index d2baf903f3..343d4ae107 100644 --- a/packages/vue-query/src/__tests__/useQuery.test.ts +++ b/packages/vue-query/src/__tests__/useQuery.test.ts @@ -23,11 +23,15 @@ describe('useQuery', () => { test('should properly execute query', () => { useQuery({ queryKey: ['key0'], queryFn: simpleFetcher, staleTime: 1000 }) - expect(useBaseQuery).toBeCalledWith(QueryObserver, { - queryKey: ['key0'], - queryFn: simpleFetcher, - staleTime: 1000, - }) + expect(useBaseQuery).toBeCalledWith( + QueryObserver, + { + queryKey: ['key0'], + queryFn: simpleFetcher, + staleTime: 1000, + }, + undefined, + ) }) test('should return loading status initially', () => { diff --git a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts index 9938e88e41..8b74e444d0 100644 --- a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts +++ b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts @@ -275,11 +275,13 @@ describe('VueQueryPlugin', () => { const fnSpy = jest.fn() - const query = useQuery({ - queryKey: ['persist'], - queryFn: fnSpy, - queryClient: customClient, - }) + const query = useQuery( + { + queryKey: ['persist'], + queryFn: fnSpy, + }, + customClient, + ) expect(customClient.isRestoring.value).toBeTruthy() expect(query.isFetching.value).toBeFalsy() @@ -325,9 +327,9 @@ describe('VueQueryPlugin', () => { { queryKey: ['persist'], queryFn: fnSpy, - queryClient: customClient, }, ], + queryClient: customClient, }) expect(customClient.isRestoring.value).toBeTruthy() diff --git a/packages/vue-query/src/types.ts b/packages/vue-query/src/types.ts index 9ae4b3ff8c..09a286828b 100644 --- a/packages/vue-query/src/types.ts +++ b/packages/vue-query/src/types.ts @@ -6,7 +6,6 @@ import type { InfiniteQueryObserverOptions, } from '@tanstack/query-core' import type { Ref, UnwrapRef } from 'vue-demi' -import type { QueryClient } from './queryClient' export type MaybeRef = Ref | T @@ -20,11 +19,6 @@ export type MaybeRefDeep = MaybeRef< : T > -export type WithQueryClientKey = T & { - queryClientKey?: string - queryClient?: QueryClient -} - // A Vue version of QueriesObserverOptions from "@tanstack/query-core" // Accept refs as options export type VueQueryObserverOptions< diff --git a/packages/vue-query/src/useBaseQuery.ts b/packages/vue-query/src/useBaseQuery.ts index 2c8d5088d0..2ab1cf49f8 100644 --- a/packages/vue-query/src/useBaseQuery.ts +++ b/packages/vue-query/src/useBaseQuery.ts @@ -17,9 +17,10 @@ import type { } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { updateState, cloneDeepUnref } from './utils' -import type { MaybeRef, WithQueryClientKey } from './types' +import type { MaybeRef } from './types' import type { UseQueryOptions } from './useQuery' import type { UseInfiniteQueryOptions } from './useInfiniteQuery' +import type { QueryClient } from './queryClient' export type UseQueryReturnType< TData, @@ -51,22 +52,22 @@ export function useBaseQuery< TData, TQueryKey >, + queryClient?: QueryClient, ): UseQueryReturnType { const options = computed(() => unrefQueryArgs(genericOptions)) - const queryClient = - options.value.queryClient ?? useQueryClient(options.value.queryClientKey) + const client = queryClient || useQueryClient() const defaultedOptions = computed(() => { - const defaulted = queryClient.defaultQueryOptions(options.value) - defaulted._optimisticResults = queryClient.isRestoring.value + const defaulted = client.defaultQueryOptions(options.value) + defaulted._optimisticResults = client.isRestoring.value ? 'isRestoring' : 'optimistic' return defaulted }) - const observer = new Observer(queryClient, defaultedOptions.value) + const observer = new Observer(client, defaultedOptions.value) const state = reactive(observer.getCurrentResult()) const unsubscribe = ref(() => { @@ -74,7 +75,7 @@ export function useBaseQuery< }) watch( - queryClient.isRestoring, + client.isRestoring, (isRestoring) => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!isRestoring) { @@ -144,11 +145,13 @@ export function unrefQueryArgs< arg1: MaybeRef< UseQueryOptionsGeneric >, -): WithQueryClientKey< - QueryObserverOptions -> { +): QueryObserverOptions { const options = unref(arg1) - return cloneDeepUnref(options) as WithQueryClientKey< - QueryObserverOptions + return cloneDeepUnref(options) as QueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey > } diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index e253d37498..531aa870dc 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -9,11 +9,8 @@ import type { import { useBaseQuery } from './useBaseQuery' import type { UseQueryReturnType } from './useBaseQuery' -import type { - WithQueryClientKey, - VueInfiniteQueryObserverOptions, - DistributiveOmit, -} from './types' +import type { VueInfiniteQueryObserverOptions, DistributiveOmit } from './types' +import type { QueryClient } from './queryClient' export type UseInfiniteQueryOptions< TQueryFnData = unknown, @@ -21,14 +18,12 @@ export type UseInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = WithRequired< - WithQueryClientKey< - VueInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - > + VueInfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey >, 'queryKey' > @@ -57,10 +52,12 @@ export function useInfiniteQuery< TQueryKey extends QueryKey = QueryKey, >( options: UseInfiniteQueryOptions, + queryClient?: QueryClient, ): UseInfiniteQueryReturnType { const result = useBaseQuery( InfiniteQueryObserver as typeof QueryObserver, options, + queryClient, ) as InfiniteQueryReturnType return { ...result, diff --git a/packages/vue-query/src/useIsFetching.ts b/packages/vue-query/src/useIsFetching.ts index 41cbfb0a28..d226df2791 100644 --- a/packages/vue-query/src/useIsFetching.ts +++ b/packages/vue-query/src/useIsFetching.ts @@ -3,25 +3,28 @@ import type { Ref } from 'vue-demi' import type { QueryFilters as QF } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { cloneDeepUnref } from './utils' -import type { MaybeRefDeep, WithQueryClientKey } from './types' +import type { MaybeRefDeep } from './types' +import type { QueryClient } from './queryClient' -export type QueryFilters = MaybeRefDeep> +export type QueryFilters = MaybeRefDeep -export function useIsFetching(fetchingFilters: QueryFilters = {}): Ref { +export function useIsFetching( + fetchingFilters: QueryFilters = {}, + queryClient?: QueryClient, +): Ref { const filters = computed(() => unrefFilterArgs(fetchingFilters)) - const queryClient = - filters.value.queryClient ?? useQueryClient(filters.value.queryClientKey) + const client = queryClient || useQueryClient() - const isFetching = ref(queryClient.isFetching(filters)) + const isFetching = ref(client.isFetching(filters)) - const unsubscribe = queryClient.getQueryCache().subscribe(() => { - isFetching.value = queryClient.isFetching(filters) + const unsubscribe = client.getQueryCache().subscribe(() => { + isFetching.value = client.isFetching(filters) }) watch( filters, () => { - isFetching.value = queryClient.isFetching(filters) + isFetching.value = client.isFetching(filters) }, { deep: true }, ) @@ -35,5 +38,5 @@ export function useIsFetching(fetchingFilters: QueryFilters = {}): Ref { export function unrefFilterArgs(arg: QueryFilters) { const options = unref(arg) - return cloneDeepUnref(options) as WithQueryClientKey + return cloneDeepUnref(options) as QF } diff --git a/packages/vue-query/src/useIsMutating.ts b/packages/vue-query/src/useIsMutating.ts index 66e94cd130..ffff46d54f 100644 --- a/packages/vue-query/src/useIsMutating.ts +++ b/packages/vue-query/src/useIsMutating.ts @@ -4,27 +4,28 @@ import type { MutationFilters as MF } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { cloneDeepUnref } from './utils' -import type { MaybeRefDeep, WithQueryClientKey } from './types' +import type { MaybeRefDeep } from './types' +import type { QueryClient } from './queryClient' -export type MutationFilters = MaybeRefDeep> +export type MutationFilters = MaybeRefDeep export function useIsMutating( mutationFilters: MutationFilters = {}, + queryClient?: QueryClient, ): Ref { const filters = computed(() => unrefFilterArgs(mutationFilters)) - const queryClient = - filters.value.queryClient ?? useQueryClient(filters.value.queryClientKey) + const client = queryClient || useQueryClient() - const isMutating = ref(queryClient.isMutating(filters)) + const isMutating = ref(client.isMutating(filters)) - const unsubscribe = queryClient.getMutationCache().subscribe(() => { - isMutating.value = queryClient.isMutating(filters) + const unsubscribe = client.getMutationCache().subscribe(() => { + isMutating.value = client.isMutating(filters) }) watch( filters, () => { - isMutating.value = queryClient.isMutating(filters) + isMutating.value = client.isMutating(filters) }, { deep: true }, ) @@ -38,5 +39,5 @@ export function useIsMutating( export function unrefFilterArgs(arg: MutationFilters) { const options = unref(arg) - return cloneDeepUnref(options) as WithQueryClientKey + return cloneDeepUnref(options) as MF } diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index 0bc892de35..6148bfcd43 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -14,15 +14,11 @@ import type { MutationObserverResult, MutationObserverOptions, } from '@tanstack/query-core' -import type { - WithQueryClientKey, - MaybeRef, - MaybeRefDeep, - DistributiveOmit, -} from './types' +import type { MaybeRef, MaybeRefDeep, DistributiveOmit } from './types' import { MutationObserver } from '@tanstack/query-core' import { cloneDeepUnref, updateState } from './utils' import { useQueryClient } from './useQueryClient' +import type { QueryClient } from './queryClient' type MutationResult = DistributiveOmit< MutationObserverResult, @@ -30,9 +26,7 @@ type MutationResult = DistributiveOmit< > export type UseMutationOptions = - WithQueryClientKey< - MutationObserverOptions - > + MutationObserverOptions export type VueMutationObserverOptions< TData = unknown, @@ -80,15 +74,15 @@ export function useMutation< mutationOptions: MaybeRef< VueMutationObserverOptions >, + queryClient?: QueryClient, ): UseMutationReturnType { const options = computed(() => { return unrefMutationArgs(mutationOptions) }) - const queryClient = - options.value.queryClient ?? useQueryClient(options.value.queryClientKey) + const client = queryClient || useQueryClient() const observer = new MutationObserver( - queryClient, - queryClient.defaultMutationOptions(options.value), + client, + client.defaultMutationOptions(options.value), ) const state = reactive(observer.getCurrentResult()) @@ -108,7 +102,7 @@ export function useMutation< watch( options, () => { - observer.setOptions(queryClient.defaultMutationOptions(options.value)) + observer.setOptions(client.defaultMutationOptions(options.value)) }, { deep: true }, ) @@ -138,9 +132,7 @@ export function unrefMutationArgs< arg: MaybeRef< VueMutationObserverOptions >, -): WithQueryClientKey< - MutationObserverOptions -> { +): MutationObserverOptions { const options = unref(arg) return cloneDeepUnref(options) as UseMutationOptions< diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index de86b08be9..e8c85b93bc 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -22,7 +22,7 @@ import type { UseQueryOptions } from './useQuery' import type { QueryClient } from './queryClient' // This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. -// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +// `placeholderData` function does not have a parameter type UseQueryOptionsForUseQueries< TQueryFnData = unknown, TError = unknown, @@ -30,7 +30,7 @@ type UseQueryOptionsForUseQueries< TQueryKey extends QueryKey = QueryKey, > = Omit< UseQueryOptions, - 'context' | 'placeholderData' + 'placeholderData' > & { placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction } @@ -153,7 +153,7 @@ type UseQueriesOptionsArg = readonly [...UseQueriesOptions] export function useQueries({ queries, - queryClient: queryClientInjected, + queryClient, }: { queries: Ref> | UseQueriesOptionsArg queryClient?: QueryClient @@ -162,27 +162,12 @@ export function useQueries({ () => cloneDeepUnref(queries) as UseQueriesOptionsArg, ) - const queryClientKey = unreffedQueries.value[0]?.queryClientKey - const optionsQueryClient = unreffedQueries.value[0]?.queryClient as - | QueryClient - | undefined - const queryClient = - queryClientInjected ?? optionsQueryClient ?? useQueryClient(queryClientKey) - if ( - process.env.NODE_ENV !== 'production' && - (queryClientKey || optionsQueryClient) - ) { - queryClient - .getLogger() - .error( - `Providing queryClient to individual queries in useQueries has been deprecated and will be removed in the next major version. You can still pass queryClient as an option directly to useQueries hook.`, - ) - } + const client = queryClient || useQueryClient() const defaultedQueries = computed(() => unreffedQueries.value.map((options) => { - const defaulted = queryClient.defaultQueryOptions(options) - defaulted._optimisticResults = queryClient.isRestoring.value + const defaulted = client.defaultQueryOptions(options) + defaulted._optimisticResults = client.isRestoring.value ? 'isRestoring' : 'optimistic' @@ -190,7 +175,7 @@ export function useQueries({ }), ) - const observer = new QueriesObserver(queryClient, defaultedQueries.value) + const observer = new QueriesObserver(client, defaultedQueries.value) const state = reactive(observer.getCurrentResult()) const unsubscribe = ref(() => { @@ -198,7 +183,7 @@ export function useQueries({ }) watch( - queryClient.isRestoring, + client.isRestoring, (isRestoring) => { if (!isRestoring) { unsubscribe.value() diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index 024c5c97a5..633b15a8cc 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -8,11 +8,8 @@ import type { } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' import type { UseQueryReturnType as UQRT } from './useBaseQuery' -import type { - WithQueryClientKey, - VueQueryObserverOptions, - DistributiveOmit, -} from './types' +import type { VueQueryObserverOptions, DistributiveOmit } from './types' +import type { QueryClient } from './queryClient' export type UseQueryReturnType = DistributiveOmit< UQRT, @@ -35,15 +32,7 @@ export type UseQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = WithRequired< - WithQueryClientKey< - VueQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey - > - >, + VueQueryObserverOptions, 'queryKey' > @@ -72,6 +61,7 @@ export function useQuery< TQueryKey extends QueryKey = QueryKey, >( options: UndefinedInitialDataOptions, + queryClient?: QueryClient, ): UseQueryReturnType export function useQuery< @@ -81,6 +71,7 @@ export function useQuery< TQueryKey extends QueryKey = QueryKey, >( options: DefinedInitialDataOptions, + queryClient?: QueryClient, ): UseQueryDefinedReturnType export function useQuery< @@ -90,10 +81,11 @@ export function useQuery< TQueryKey extends QueryKey = QueryKey, >( options: UseQueryOptions, + queryClient?: QueryClient, ): | UseQueryReturnType | UseQueryDefinedReturnType { - const result = useBaseQuery(QueryObserver, options) + const result = useBaseQuery(QueryObserver, options, queryClient) return { ...result, From 8c373d24eff1ac207ba093d463a339f6677e878b Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 20 Jan 2023 21:54:30 +0100 Subject: [PATCH 021/314] fix: all Promise rejections must be Errors (#4843) * fix: make sure all promise rejections are Errors because our default TError type is now Error * fix: make sure all promise rejections are Errors because our default TError type is now Error * remove string promise rejection I can't say I fully understand this code, but whatever we reject here does not land up in our reducer because we have already rejected with a CancelledError the rejection here is just necessary to stop further pages from fetching after the AbortSignal has been consumed * fix another test * fix types * fix createQuery tests --- .../query-core/src/infiniteQueryBehavior.ts | 5 +- packages/query-core/src/mutation.ts | 2 +- packages/query-core/src/query.ts | 2 +- .../query-core/src/tests/hydration.test.tsx | 6 +- .../src/tests/infiniteQueryBehavior.test.tsx | 2 +- .../src/tests/mutationCache.test.tsx | 11 ++- .../query-core/src/tests/mutations.test.tsx | 10 +-- packages/query-core/src/tests/query.test.tsx | 2 +- .../src/__tests__/useMutation.test.tsx | 28 +++---- .../src/__tests__/useQuery.test.tsx | 77 +++++++++---------- .../src/__tests__/createMutation.test.tsx | 24 +++--- .../src/__tests__/createQuery.test.tsx | 66 ++++++++-------- 12 files changed, 118 insertions(+), 117 deletions(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index b6989f0eb0..667d8cda75 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -44,7 +44,8 @@ export function infiniteQueryBehavior< // Get query function const queryFn = - context.options.queryFn || (() => Promise.reject('Missing queryFn')) + context.options.queryFn || + (() => Promise.reject(new Error('Missing queryFn'))) const buildNewPages = ( pages: unknown[], @@ -66,7 +67,7 @@ export function infiniteQueryBehavior< previous?: boolean, ): Promise => { if (cancelled) { - return Promise.reject('Cancelled') + return Promise.reject() } if (typeof param === 'undefined' && !manual && pages.length) { diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 5e693a3ca9..e6404aba58 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -162,7 +162,7 @@ export class Mutation< this.#retryer = createRetryer({ fn: () => { if (!this.options.mutationFn) { - return Promise.reject('No mutationFn found') + return Promise.reject(new Error('No mutationFn found')) } return this.options.mutationFn(this.state.variables!) }, diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 3a1144b5f0..8049a96519 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -389,7 +389,7 @@ export class Query< // Create fetch function const fetchFn = () => { if (!this.options.queryFn) { - return Promise.reject('Missing queryFn') + return Promise.reject(new Error('Missing queryFn')) } this.#abortSignalConsumed = false return this.options.queryFn( diff --git a/packages/query-core/src/tests/hydration.test.tsx b/packages/query-core/src/tests/hydration.test.tsx index 2bf773b949..cf23bf8986 100644 --- a/packages/query-core/src/tests/hydration.test.tsx +++ b/packages/query-core/src/tests/hydration.test.tsx @@ -347,7 +347,7 @@ describe('dehydration and rehydration', () => { const serverAddTodo = jest .fn() - .mockImplementation(() => Promise.reject('offline')) + .mockImplementation(() => Promise.reject(new Error('offline'))) const serverOnMutate = jest.fn().mockImplementation((variables) => { const optimisticTodo = { id: 1, text: variables.text } return { optimisticTodo } @@ -424,7 +424,7 @@ describe('dehydration and rehydration', () => { const serverAddTodo = jest .fn() - .mockImplementation(() => Promise.reject('offline')) + .mockImplementation(() => Promise.reject(new Error('offline'))) const queryClient = createQueryClient() @@ -453,7 +453,7 @@ describe('dehydration and rehydration', () => { const serverAddTodo = jest .fn() - .mockImplementation(() => Promise.reject('offline')) + .mockImplementation(() => Promise.reject(new Error('offline'))) const queryClient = createQueryClient() diff --git a/packages/query-core/src/tests/infiniteQueryBehavior.test.tsx b/packages/query-core/src/tests/infiniteQueryBehavior.test.tsx index f52023136a..9f4694f7d2 100644 --- a/packages/query-core/src/tests/infiniteQueryBehavior.test.tsx +++ b/packages/query-core/src/tests/infiniteQueryBehavior.test.tsx @@ -37,7 +37,7 @@ describe('InfiniteQueryBehavior', () => { await waitFor(() => { return expect(observerResult).toMatchObject({ isError: true, - error: 'Missing queryFn', + error: new Error('Missing queryFn'), }) }) diff --git a/packages/query-core/src/tests/mutationCache.test.tsx b/packages/query-core/src/tests/mutationCache.test.tsx index 6636da10eb..e6366d6e86 100644 --- a/packages/query-core/src/tests/mutationCache.test.tsx +++ b/packages/query-core/src/tests/mutationCache.test.tsx @@ -14,13 +14,18 @@ describe('mutationCache', () => { await executeMutation(testClient, { mutationKey: key, variables: 'vars', - mutationFn: () => Promise.reject('error'), + mutationFn: () => Promise.reject(new Error('error')), onMutate: () => 'context', }) } catch {} const mutation = testCache.getAll()[0] - expect(onError).toHaveBeenCalledWith('error', 'vars', 'context', mutation) + expect(onError).toHaveBeenCalledWith( + new Error('error'), + 'vars', + 'context', + mutation, + ) }) test('should be awaited', async () => { @@ -38,7 +43,7 @@ describe('mutationCache', () => { await executeMutation(testClient, { mutationKey: key, variables: 'vars', - mutationFn: () => Promise.reject('error'), + mutationFn: () => Promise.reject(new Error('error')), onError: async () => { states.push(3) await sleep(1) diff --git a/packages/query-core/src/tests/mutations.test.tsx b/packages/query-core/src/tests/mutations.test.tsx index b281259f3e..aa8fa03698 100644 --- a/packages/query-core/src/tests/mutations.test.tsx +++ b/packages/query-core/src/tests/mutations.test.tsx @@ -153,7 +153,7 @@ describe('mutations', () => { const mutation = new MutationObserver(queryClient, { mutationFn: async () => { await sleep(20) - return Promise.reject('err') + return Promise.reject(new Error('err')) }, onMutate: (text) => text, variables: 'todo', @@ -214,7 +214,7 @@ describe('mutations', () => { data: undefined, error: null, failureCount: 1, - failureReason: 'err', + failureReason: new Error('err'), isError: false, isIdle: false, isLoading: true, @@ -231,9 +231,9 @@ describe('mutations', () => { expect(states[3]).toEqual({ context: 'todo', data: undefined, - error: 'err', + error: new Error('err'), failureCount: 2, - failureReason: 'err', + failureReason: new Error('err'), isError: true, isIdle: false, isLoading: false, @@ -340,6 +340,6 @@ describe('mutations', () => { } catch (err) { error = err } - expect(error).toEqual('No mutationFn found') + expect(error).toEqual(new Error('No mutationFn found')) }) }) diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 145e79a8ed..7af305d964 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -761,7 +761,7 @@ describe('query', () => { const unsubscribe = observer.subscribe(() => undefined) await sleep(10) - expect(mockLogger.error).toHaveBeenCalledWith('Missing queryFn') + expect(mockLogger.error).toHaveBeenCalledWith(new Error('Missing queryFn')) unsubscribe() }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index ad57049ee4..20c7f8b331 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -154,7 +154,7 @@ describe('useMutation', () => { const mutateFn = jest.fn, [value: Value]>() mutateFn.mockImplementationOnce(() => { - return Promise.reject('Error test Jonas') + return Promise.reject(new Error('Error test Jonas')) }) mutateFn.mockImplementation(async (value) => { @@ -163,18 +163,16 @@ describe('useMutation', () => { }) function Page() { - const { mutate, failureCount, failureReason, data, status } = useMutation< - Value, - string, - Value - >({ mutationFn: mutateFn }) + const { mutate, failureCount, failureReason, data, status } = useMutation( + { mutationFn: mutateFn }, + ) return (

    Data {data?.count}

    Status {status}

    Failed {failureCount} times

    -

    Failed because {failureReason ?? 'null'}

    +

    Failed because {failureReason?.message ?? 'null'}

    ) @@ -318,7 +316,7 @@ describe('useMutation', () => { function Page() { const { mutateAsync } = useMutation({ - mutationFn: async (_text: string) => Promise.reject('oops'), + mutationFn: async (_text: string) => Promise.reject(new Error('oops')), onError: async () => { callbacks.push('useMutation.onError') }, @@ -339,7 +337,7 @@ describe('useMutation', () => { }, }) } catch (error) { - callbacks.push(`mutateAsync.error:${error}`) + callbacks.push(`mutateAsync.error:${(error as Error).message}`) } }, 10) }, [mutateAsync]) @@ -405,7 +403,7 @@ describe('useMutation', () => { const { mutate } = useMutation({ mutationFn: (_text: string) => { count++ - return Promise.reject('oops') + return Promise.reject(new Error('oops')) }, retry: 1, retryDelay: 5, @@ -594,7 +592,9 @@ describe('useMutation', () => { mutationFn: async (_text: string) => { await sleep(1) count++ - return count > 1 ? Promise.resolve('data') : Promise.reject('oops') + return count > 1 + ? Promise.resolve('data') + : Promise.reject(new Error('oops')) }, retry: 1, retryDelay: 5, @@ -635,13 +635,13 @@ describe('useMutation', () => { isLoading: true, isPaused: false, failureCount: 1, - failureReason: 'oops', + failureReason: new Error('oops'), }) expect(states[3]).toMatchObject({ isLoading: true, isPaused: true, failureCount: 1, - failureReason: 'oops', + failureReason: new Error('oops'), }) onlineMock.mockReturnValue(true) @@ -654,7 +654,7 @@ describe('useMutation', () => { isLoading: true, isPaused: false, failureCount: 1, - failureReason: 'oops', + failureReason: new Error('oops'), }) expect(states[5]).toMatchObject({ isLoading: false, diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 2ef6e8bb20..458a5a06ad 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -304,12 +304,12 @@ describe('useQuery', () => { it('should return the correct states for an unsuccessful query', async () => { const key = queryKey() - const states: UseQueryResult[] = [] + const states: UseQueryResult[] = [] function Page() { - const state = useQuery({ + const state = useQuery({ queryKey: key, - queryFn: () => Promise.reject('rejected'), + queryFn: () => Promise.reject(new Error('rejected')), retry: 1, retryDelay: 1, @@ -321,7 +321,7 @@ describe('useQuery', () => {

    Status: {state.status}

    Failure Count: {state.failureCount}
    -
    Failure Reason: {state.failureReason}
    +
    Failure Reason: {state.failureReason?.message}
    ) } @@ -362,7 +362,7 @@ describe('useQuery', () => { error: null, errorUpdatedAt: 0, failureCount: 1, - failureReason: 'rejected', + failureReason: new Error('rejected'), errorUpdateCount: 0, isError: false, isFetched: false, @@ -385,10 +385,10 @@ describe('useQuery', () => { expect(states[2]).toEqual({ data: undefined, dataUpdatedAt: 0, - error: 'rejected', + error: new Error('rejected'), errorUpdatedAt: expect.any(Number), failureCount: 2, - failureReason: 'rejected', + failureReason: new Error('rejected'), errorUpdateCount: 1, isError: true, isFetched: true, @@ -590,7 +590,7 @@ describe('useQuery', () => { function Page() { const state = useQuery({ queryKey: key, - queryFn: () => Promise.reject('error'), + queryFn: () => Promise.reject(new Error('error')), retry: false, onError, }) @@ -603,7 +603,7 @@ describe('useQuery', () => { await sleep(10) expect(states.length).toBe(2) expect(onError).toHaveBeenCalledTimes(1) - expect(onError).toHaveBeenCalledWith('error') + expect(onError).toHaveBeenCalledWith(new Error('error')) }) it('should not call onError when receiving a CancelledError', async () => { @@ -2869,10 +2869,10 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const { status, error } = useQuery({ + const { status, error } = useQuery({ queryKey: key, queryFn: () => { - return Promise.reject('Error test jaylen') + return Promise.reject(new Error('Error test jaylen')) }, retry: false, }) @@ -2880,7 +2880,7 @@ describe('useQuery', () => { return (

    {status}

    -

    {error}

    +

    {error?.message}

    ) } @@ -2897,7 +2897,7 @@ describe('useQuery', () => { function Page() { const { status, error } = useQuery({ queryKey: key, - queryFn: () => Promise.reject('Error test jaylen'), + queryFn: () => Promise.reject(new Error('Error test jaylen')), retry: false, throwErrors: true, }) @@ -3023,12 +3023,12 @@ describe('useQuery', () => { let count = 0 function Page() { - const result = useQuery({ + const result = useQuery({ queryKey: key, queryFn: async () => { count++ await sleep(10) - return Promise.reject('some error') + return Promise.reject(new Error('some error')) }, retry: 2, @@ -3037,9 +3037,9 @@ describe('useQuery', () => { return (
    -
    error: {result.error ?? 'null'}
    +
    error: {result.error?.message ?? 'null'}
    failureCount: {result.failureCount}
    -
    failureReason: {result.failureReason}
    +
    failureReason: {result.failureReason?.message}
    ) } @@ -3072,12 +3072,12 @@ describe('useQuery', () => { let count = 0 function Page() { - const result = useQuery({ + const result = useQuery({ queryKey: key, queryFn: async () => { count++ await sleep(10) - return Promise.reject('some error') + return Promise.reject(new Error('some error')) }, retry: 2, @@ -3086,9 +3086,9 @@ describe('useQuery', () => { return (
    -
    error: {result.error ?? 'null'}
    +
    error: {result.error?.message ?? 'null'}
    failureCount: {result.failureCount}
    -
    failureReason: {result.failureReason}
    +
    failureReason: {result.failureReason?.message}
    ) } @@ -3345,25 +3345,22 @@ describe('useQuery', () => { const queryFn = jest.fn() queryFn.mockImplementation(() => { - return Promise.reject('Error test Barrett') + return Promise.reject(new Error('Error test Barrett')) }) function Page() { - const { status, failureCount, failureReason } = useQuery( - { - queryKey: key, - queryFn, - - retry: 1, - retryDelay: 1, - }, - ) + const { status, failureCount, failureReason } = useQuery({ + queryKey: key, + queryFn, + retry: 1, + retryDelay: 1, + }) return (

    {status}

    Failed {failureCount} times

    -

    Failed because {failureReason}

    +

    Failed because {failureReason?.message}

    ) } @@ -3386,31 +3383,27 @@ describe('useQuery', () => { const queryFn = jest.fn() queryFn.mockImplementationOnce(() => { - return Promise.reject('Error test Tanner') + return Promise.reject(new Error('Error test Tanner')) }) queryFn.mockImplementation(() => { - return Promise.reject('NoRetry') + return Promise.reject(new Error('NoRetry')) }) function Page() { - const { status, failureCount, failureReason, error } = useQuery< - unknown, - string, - [string] - >({ + const { status, failureCount, failureReason, error } = useQuery({ queryKey: key, queryFn, retryDelay: 1, - retry: (_failureCount, err) => err !== 'NoRetry', + retry: (_failureCount, err) => err.message !== 'NoRetry', }) return (

    {status}

    Failed {failureCount} times

    -

    Failed because {failureReason}

    -

    {error}

    +

    Failed because {failureReason?.message}

    +

    {error?.message}

    ) } diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index 1dab157bfa..d800bde66f 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -21,7 +21,7 @@ import { sleep, } from './utils' -describe('useMutation', () => { +describe('createMutation', () => { const queryCache = new QueryCache() const mutationCache = new MutationCache() const queryClient = createQueryClient({ queryCache, mutationCache }) @@ -177,7 +177,7 @@ describe('useMutation', () => { const mutateFn = jest.fn, [value: Value]>() mutateFn.mockImplementationOnce(() => { - return Promise.reject('Error test Jonas') + return Promise.reject(new Error('Error test Jonas')) }) mutateFn.mockImplementation(async (value) => { @@ -186,7 +186,7 @@ describe('useMutation', () => { }) function Page() { - const mutation = createMutation(() => ({ + const mutation = createMutation(() => ({ mutationFn: mutateFn, })) @@ -195,7 +195,7 @@ describe('useMutation', () => {

    Data {mutation.data?.count}

    Status {mutation.status}

    Failed {mutation.failureCount} times

    -

    Failed because {mutation.failureReason ?? 'null'}

    +

    Failed because {mutation.failureReason?.message ?? 'null'}

    +
    +{/if} +{#if $todos.isFetching} +
    + 'Background Updating...' : ' ' +
    +{/if} + + diff --git a/examples/svelte/auto-refetching/src/routes/api/data/+server.ts b/examples/svelte/auto-refetching/src/routes/api/data/+server.ts new file mode 100644 index 0000000000..5b12a9d23c --- /dev/null +++ b/examples/svelte/auto-refetching/src/routes/api/data/+server.ts @@ -0,0 +1,19 @@ +import { json, type RequestHandler } from '@sveltejs/kit' + +let list = { items: ['Item 1', 'Item 2', 'Item 3'] } + +/** @type {import('./$types').RequestHandler} */ +export const GET: RequestHandler = async ({ url }) => { + const add = url.searchParams.get('add') + const clear = url.searchParams.get('clear') + + if (add) { + if (!list.items.includes(add)) { + list.items.push(add) + } + } else if (clear) { + list.items = [] + } + await new Promise((r) => setTimeout(r, 1000)) + return json(list, { status: 200 }) +} diff --git a/examples/svelte/auto-refetching/static/emblem-light.svg b/examples/svelte/auto-refetching/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/auto-refetching/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/auto-refetching/svelte.config.js b/examples/svelte/auto-refetching/svelte.config.js new file mode 100644 index 0000000000..836e30422d --- /dev/null +++ b/examples/svelte/auto-refetching/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/auto-refetching/tsconfig.json b/examples/svelte/auto-refetching/tsconfig.json new file mode 100644 index 0000000000..794b95b642 --- /dev/null +++ b/examples/svelte/auto-refetching/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/svelte/auto-refetching/vite.config.ts b/examples/svelte/auto-refetching/vite.config.ts new file mode 100644 index 0000000000..f2eb6d93cf --- /dev/null +++ b/examples/svelte/auto-refetching/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/basic/.gitignore b/examples/svelte/basic/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/examples/svelte/basic/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/examples/svelte/basic/README.md b/examples/svelte/basic/README.md new file mode 100644 index 0000000000..5c91169b0c --- /dev/null +++ b/examples/svelte/basic/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/examples/svelte/basic/package.json b/examples/svelte/basic/package.json new file mode 100644 index 0000000000..49d0649853 --- /dev/null +++ b/examples/svelte/basic/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-basic", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/basic/sandbox.config.json b/examples/svelte/basic/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/basic/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/basic/src/app.css b/examples/svelte/basic/src/app.css new file mode 100644 index 0000000000..d301f1b2a3 --- /dev/null +++ b/examples/svelte/basic/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +main { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +.button:hover { + border-color: #646cff; +} +.button:focus, +.button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + .button { + background-color: #f9f9f9; + } +} diff --git a/examples/svelte/basic/src/app.d.ts b/examples/svelte/basic/src/app.d.ts new file mode 100644 index 0000000000..3e4ed2057b --- /dev/null +++ b/examples/svelte/basic/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Locals {} + // interface PageData {} + // interface Error {} + // interface Platform {} +} diff --git a/examples/svelte/basic/src/app.html b/examples/svelte/basic/src/app.html new file mode 100644 index 0000000000..bf205c1085 --- /dev/null +++ b/examples/svelte/basic/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/basic/src/lib/Post.svelte b/examples/svelte/basic/src/lib/Post.svelte new file mode 100644 index 0000000000..7e077b7037 --- /dev/null +++ b/examples/svelte/basic/src/lib/Post.svelte @@ -0,0 +1,31 @@ + + +
    +
    + Back +
    + {#if !postId || $post.isLoading} + Loading... + {/if} + {#if $post.error} + Error: {$post.error.message} + {/if} + {#if $post.isSuccess} +

    {$post.data.title}

    +
    +

    {$post.data.body}

    +
    +
    {$post.isFetching ? 'Background Updating...' : ' '}
    + {/if} +
    diff --git a/examples/svelte/basic/src/lib/Posts.svelte b/examples/svelte/basic/src/lib/Posts.svelte new file mode 100644 index 0000000000..83b4112f66 --- /dev/null +++ b/examples/svelte/basic/src/lib/Posts.svelte @@ -0,0 +1,60 @@ + + +
    +
    + {#if $posts.status === 'loading'} + Loading... + {:else if $posts.status === 'error'} + Error: {$posts.error.message} + {:else} + + {#if $posts.isFetching} +
    + Background Updating... +
    + {/if} + {/if} +
    +
    + + diff --git a/examples/svelte/basic/src/lib/data.ts b/examples/svelte/basic/src/lib/data.ts new file mode 100644 index 0000000000..eed8ecbb1d --- /dev/null +++ b/examples/svelte/basic/src/lib/data.ts @@ -0,0 +1,15 @@ +import type { Post } from './types' + +export const getPosts = async (limit: number) => { + const response = await fetch('https://jsonplaceholder.typicode.com/posts') + const data = (await response.json()) as Post[] + return data.filter((x) => x.id <= limit) +} + +export const getPostById = async (id: number): Promise => { + const response = await fetch( + `https://jsonplaceholder.typicode.com/posts/${id}`, + ) + const data = (await response.json()) as Post + return data +} diff --git a/examples/svelte/basic/src/lib/types.ts b/examples/svelte/basic/src/lib/types.ts new file mode 100644 index 0000000000..fb3835e9ff --- /dev/null +++ b/examples/svelte/basic/src/lib/types.ts @@ -0,0 +1,5 @@ +export type Post = { + id: number + title: string + body: string +} diff --git a/examples/svelte/basic/src/routes/+layout.svelte b/examples/svelte/basic/src/routes/+layout.svelte new file mode 100644 index 0000000000..8c686d17ed --- /dev/null +++ b/examples/svelte/basic/src/routes/+layout.svelte @@ -0,0 +1,19 @@ + + + +
    + +
    +
    diff --git a/examples/svelte/basic/src/routes/+page.svelte b/examples/svelte/basic/src/routes/+page.svelte new file mode 100644 index 0000000000..293f360067 --- /dev/null +++ b/examples/svelte/basic/src/routes/+page.svelte @@ -0,0 +1,6 @@ + + +

    Basic Query

    + diff --git a/examples/svelte/basic/src/routes/[postId]/+page.svelte b/examples/svelte/basic/src/routes/[postId]/+page.svelte new file mode 100644 index 0000000000..b68acc0bc0 --- /dev/null +++ b/examples/svelte/basic/src/routes/[postId]/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/examples/svelte/basic/src/routes/[postId]/+page.ts b/examples/svelte/basic/src/routes/[postId]/+page.ts new file mode 100644 index 0000000000..ea67748ad4 --- /dev/null +++ b/examples/svelte/basic/src/routes/[postId]/+page.ts @@ -0,0 +1,6 @@ +import type { PageLoad } from './$types' + +export const load: PageLoad = async ({ params }) => { + const postId = parseInt(params.postId) + return { postId } +} diff --git a/examples/svelte/basic/static/emblem-light.svg b/examples/svelte/basic/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/basic/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/basic/svelte.config.js b/examples/svelte/basic/svelte.config.js new file mode 100644 index 0000000000..836e30422d --- /dev/null +++ b/examples/svelte/basic/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/basic/tsconfig.json b/examples/svelte/basic/tsconfig.json new file mode 100644 index 0000000000..794b95b642 --- /dev/null +++ b/examples/svelte/basic/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/svelte/basic/vite.config.ts b/examples/svelte/basic/vite.config.ts new file mode 100644 index 0000000000..f2eb6d93cf --- /dev/null +++ b/examples/svelte/basic/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/load-more-infinite-scroll/.gitignore b/examples/svelte/load-more-infinite-scroll/.gitignore new file mode 100644 index 0000000000..6635cf5542 --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/examples/svelte/load-more-infinite-scroll/README.md b/examples/svelte/load-more-infinite-scroll/README.md new file mode 100644 index 0000000000..5c91169b0c --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/examples/svelte/load-more-infinite-scroll/package.json b/examples/svelte/load-more-infinite-scroll/package.json new file mode 100644 index 0000000000..29f4191601 --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-load-more-infinite-scroll", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/load-more-infinite-scroll/sandbox.config.json b/examples/svelte/load-more-infinite-scroll/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/load-more-infinite-scroll/src/app.css b/examples/svelte/load-more-infinite-scroll/src/app.css new file mode 100644 index 0000000000..bcc7233dd1 --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/svelte/load-more-infinite-scroll/src/app.d.ts b/examples/svelte/load-more-infinite-scroll/src/app.d.ts new file mode 100644 index 0000000000..1cea0dcf2b --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} +} diff --git a/examples/svelte/load-more-infinite-scroll/src/app.html b/examples/svelte/load-more-infinite-scroll/src/app.html new file mode 100644 index 0000000000..117bd02615 --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/load-more-infinite-scroll/src/lib/LoadMore.svelte b/examples/svelte/load-more-infinite-scroll/src/lib/LoadMore.svelte new file mode 100644 index 0000000000..743d8da7cc --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/lib/LoadMore.svelte @@ -0,0 +1,66 @@ + + +{#if $query.isLoading} + Loading... +{/if} +{#if $query.error} + Error: {error.message} +{/if} +{#if $query.isSuccess} +
    + {#each $query.data.pages as { results }} + {#each results as planet} +
    +
    +

    Planet Name: {planet.name}

    +

    Population: {planet.population}

    +
    +
    + {/each} + {/each} +
    +
    + +
    +{/if} + + diff --git a/examples/svelte/load-more-infinite-scroll/src/routes/+layout.svelte b/examples/svelte/load-more-infinite-scroll/src/routes/+layout.svelte new file mode 100644 index 0000000000..8c686d17ed --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/routes/+layout.svelte @@ -0,0 +1,19 @@ + + + +
    + +
    +
    diff --git a/examples/svelte/load-more-infinite-scroll/src/routes/+page.svelte b/examples/svelte/load-more-infinite-scroll/src/routes/+page.svelte new file mode 100644 index 0000000000..14c2b4abbd --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/src/routes/+page.svelte @@ -0,0 +1,6 @@ + + +

    Infinte Load More

    + diff --git a/examples/svelte/load-more-infinite-scroll/static/emblem-light.svg b/examples/svelte/load-more-infinite-scroll/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/load-more-infinite-scroll/svelte.config.js b/examples/svelte/load-more-infinite-scroll/svelte.config.js new file mode 100644 index 0000000000..05e80f6c7d --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/load-more-infinite-scroll/tsconfig.json b/examples/svelte/load-more-infinite-scroll/tsconfig.json new file mode 100644 index 0000000000..794b95b642 --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/svelte/load-more-infinite-scroll/vite.config.ts b/examples/svelte/load-more-infinite-scroll/vite.config.ts new file mode 100644 index 0000000000..f2eb6d93cf --- /dev/null +++ b/examples/svelte/load-more-infinite-scroll/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/optimistic-updates-typescript/.gitignore b/examples/svelte/optimistic-updates-typescript/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/examples/svelte/optimistic-updates-typescript/README.md b/examples/svelte/optimistic-updates-typescript/README.md new file mode 100644 index 0000000000..5c91169b0c --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/examples/svelte/optimistic-updates-typescript/package.json b/examples/svelte/optimistic-updates-typescript/package.json new file mode 100644 index 0000000000..4ace661ad7 --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-optimistic-updates-typescript", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/optimistic-updates-typescript/sandbox.config.json b/examples/svelte/optimistic-updates-typescript/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/optimistic-updates-typescript/src/app.css b/examples/svelte/optimistic-updates-typescript/src/app.css new file mode 100644 index 0000000000..c57658b1ef --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +main { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/svelte/optimistic-updates-typescript/src/app.d.ts b/examples/svelte/optimistic-updates-typescript/src/app.d.ts new file mode 100644 index 0000000000..3e4ed2057b --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Locals {} + // interface PageData {} + // interface Error {} + // interface Platform {} +} diff --git a/examples/svelte/optimistic-updates-typescript/src/app.html b/examples/svelte/optimistic-updates-typescript/src/app.html new file mode 100644 index 0000000000..ab042806e7 --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/optimistic-updates-typescript/src/routes/+layout.svelte b/examples/svelte/optimistic-updates-typescript/src/routes/+layout.svelte new file mode 100644 index 0000000000..8c686d17ed --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/routes/+layout.svelte @@ -0,0 +1,19 @@ + + + +
    + +
    +
    diff --git a/examples/svelte/optimistic-updates-typescript/src/routes/+page.svelte b/examples/svelte/optimistic-updates-typescript/src/routes/+page.svelte new file mode 100644 index 0000000000..41e68eb369 --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/routes/+page.svelte @@ -0,0 +1,129 @@ + + +

    Optimistic Updates

    +

    + In this example, new items can be created using a mutation. The new item will + be optimistically added to the list in hopes that the server accepts the item. + If it does, the list is refetched with the true items from the list. Every now + and then, the mutation may fail though. When that happens, the previous list + of items is restored and the list is again refetched from the server. +

    + +
    { + e.preventDefault() + e.stopPropagation() + $addTodoMutation.mutate(text) + }} +> +
    + + +
    +
    + +{#if $todos.isLoading} + Loading... +{/if} +{#if $todos.error} + An error has occurred: + {$todos.error.message} +{/if} +{#if $todos.isSuccess} +
    + Updated At: {new Date($todos.data.ts).toLocaleTimeString()} +
    +
      + {#each $todos.data.items as todo} +
    • {todo.text}
    • + {/each} +
    +{/if} +{#if $todos.isFetching} +
    + 'Background Updating...' : ' ' +
    +{/if} + + diff --git a/examples/svelte/optimistic-updates-typescript/src/routes/api/data/+server.ts b/examples/svelte/optimistic-updates-typescript/src/routes/api/data/+server.ts new file mode 100644 index 0000000000..301018f5cc --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/src/routes/api/data/+server.ts @@ -0,0 +1,29 @@ +import { json, type RequestHandler } from '@sveltejs/kit' + +type Todo = { + id: string + text: string +} + +const items: Todo[] = [] + +/** @type {import('./$types').RequestHandler} */ +export const GET: RequestHandler = async (req) => { + await new Promise((r) => setTimeout(r, 1000)) + return json({ ts: Date.now(), items }, { status: 200 }) +} + +/** @type {import('./$types').RequestHandler} */ +export const POST: RequestHandler = async ({ request }) => { + const { text } = await request.json() + + if (Math.random() > 0.7) { + json({ message: 'Could not add item!' }, { status: 500 }) + } + const newTodo = { + id: Math.random().toString(), + text: text.toUpperCase() as string, + } + items.push(newTodo) + return json(newTodo, { status: 200 }) +} diff --git a/examples/svelte/optimistic-updates-typescript/static/emblem-light.svg b/examples/svelte/optimistic-updates-typescript/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/optimistic-updates-typescript/svelte.config.js b/examples/svelte/optimistic-updates-typescript/svelte.config.js new file mode 100644 index 0000000000..836e30422d --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/optimistic-updates-typescript/tsconfig.json b/examples/svelte/optimistic-updates-typescript/tsconfig.json new file mode 100644 index 0000000000..794b95b642 --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/svelte/optimistic-updates-typescript/vite.config.ts b/examples/svelte/optimistic-updates-typescript/vite.config.ts new file mode 100644 index 0000000000..f2eb6d93cf --- /dev/null +++ b/examples/svelte/optimistic-updates-typescript/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/playground/.gitignore b/examples/svelte/playground/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/examples/svelte/playground/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/examples/svelte/playground/README.md b/examples/svelte/playground/README.md new file mode 100644 index 0000000000..678586e2c5 --- /dev/null +++ b/examples/svelte/playground/README.md @@ -0,0 +1,8 @@ +# Example + +This example is a rewrite of the React Query playground example. + +To run this example: + +- `npm install` +- `npm run dev` diff --git a/examples/svelte/playground/package.json b/examples/svelte/playground/package.json new file mode 100644 index 0000000000..e34b0c2d37 --- /dev/null +++ b/examples/svelte/playground/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-playground", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/playground/sandbox.config.json b/examples/svelte/playground/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/playground/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/playground/src/app.css b/examples/svelte/playground/src/app.css new file mode 100644 index 0000000000..eb5c0a46ab --- /dev/null +++ b/examples/svelte/playground/src/app.css @@ -0,0 +1,11 @@ +body { + margin: 0; + padding: 1rem; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: white; + background: #0b1521; +} diff --git a/examples/svelte/playground/src/app.d.ts b/examples/svelte/playground/src/app.d.ts new file mode 100644 index 0000000000..1cea0dcf2b --- /dev/null +++ b/examples/svelte/playground/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} +} diff --git a/examples/svelte/playground/src/app.html b/examples/svelte/playground/src/app.html new file mode 100644 index 0000000000..d847151a12 --- /dev/null +++ b/examples/svelte/playground/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/playground/src/lib/stores.ts b/examples/svelte/playground/src/lib/stores.ts new file mode 100644 index 0000000000..be3774f8bb --- /dev/null +++ b/examples/svelte/playground/src/lib/stores.ts @@ -0,0 +1,23 @@ +import { writable } from 'svelte/store' + +export const staleTime = writable(1000) +export const cacheTime = writable(3000) +export const errorRate = writable(0.05) +export const queryTimeMin = writable(1000) +export const queryTimeMax = writable(2000) + +export const editingIndex = writable(null) +export const views = writable(['', 'fruit', 'grape']) + +let initialId = 0 +const initialList = [ + 'apple', + 'banana', + 'pineapple', + 'grapefruit', + 'dragonfruit', + 'grapes', +].map((d) => ({ id: initialId++, name: d, notes: 'These are some notes' })) + +export const list = writable(initialList) +export const id = writable(initialId) diff --git a/examples/svelte/playground/src/routes/+layout.svelte b/examples/svelte/playground/src/routes/+layout.svelte new file mode 100644 index 0000000000..8219a09ca9 --- /dev/null +++ b/examples/svelte/playground/src/routes/+layout.svelte @@ -0,0 +1,23 @@ + + + + Svelte Query Playground Example + + + +
    + +
    +
    diff --git a/examples/svelte/playground/src/routes/+page.svelte b/examples/svelte/playground/src/routes/+page.svelte new file mode 100644 index 0000000000..e222011bb2 --- /dev/null +++ b/examples/svelte/playground/src/routes/+page.svelte @@ -0,0 +1,81 @@ + + +

    + The "staleTime" and "cacheTime" durations have been altered in this example to + show how query stale-ness and query caching work on a granular level +

    +
    + Stale Time:{' '} + +
    +
    + Cache Time:{' '} + +
    +
    +
    + Error Rate:{' '} + +
    +
    + Fetch Time Min:{' '} + {' '} +
    +
    + Fetch Time Max:{' '} + +
    + +
    + +
    diff --git a/examples/svelte/playground/src/routes/AddTodo.svelte b/examples/svelte/playground/src/routes/AddTodo.svelte new file mode 100644 index 0000000000..c92fc2e47f --- /dev/null +++ b/examples/svelte/playground/src/routes/AddTodo.svelte @@ -0,0 +1,57 @@ + + +
    + + + + +
    + {$addMutation.status === 'loading' + ? 'Saving...' + : $addMutation.status === 'error' + ? $addMutation.error.message + : 'Saved!'} +
    +
    diff --git a/examples/svelte/playground/src/routes/App.svelte b/examples/svelte/playground/src/routes/App.svelte new file mode 100644 index 0000000000..98fb2ad97d --- /dev/null +++ b/examples/svelte/playground/src/routes/App.svelte @@ -0,0 +1,42 @@ + + +
    +
    + +
    +
    +
    + + {#each $views as view} +
    + +
    +
    + {/each} + + +
    + + {#if $editingIndex !== null} + +
    + {/if} + + +
    diff --git a/examples/svelte/playground/src/routes/EditTodo.svelte b/examples/svelte/playground/src/routes/EditTodo.svelte new file mode 100644 index 0000000000..fdf249a334 --- /dev/null +++ b/examples/svelte/playground/src/routes/EditTodo.svelte @@ -0,0 +1,118 @@ + + +
    +
    + {#if $query.data} + Editing Todo + "{$query.data.name}" (#{$editingIndex}) + {/if} +
    + {#if $query.status === 'loading'} + Loading... (Attempt: {$query.failureCount + 1}) + {:else if $query.error} + + Error! + + {:else} + + +
    + +
    +
    + {$saveMutation.status === 'loading' + ? 'Saving...' + : $saveMutation.status === 'error' + ? $saveMutation.error.message + : 'Saved!'} +
    +
    + {#if $query.isFetching} + + Background Refreshing... (Attempt: {$query.failureCount + 1}) + + {:else} +   + {/if} +
    + {/if} +
    diff --git a/examples/svelte/playground/src/routes/Todos.svelte b/examples/svelte/playground/src/routes/Todos.svelte new file mode 100644 index 0000000000..f3537d7c50 --- /dev/null +++ b/examples/svelte/playground/src/routes/Todos.svelte @@ -0,0 +1,71 @@ + + +
    + +
    + +{#if $query.status === 'loading'} + Loading... (Attempt: {$query.failureCount + 1}) +{:else if $query.status === 'error'} + + Error: {$query.error.message} +
    + +
    +{:else} +
      + {#if $query.data} + {#each $query.data as todo} +
    • + {todo.name}{' '} + +
    • + {/each} + {/if} +
    +
    + {#if $query.isFetching} + + Background Refreshing... (Attempt: {$query.failureCount + 1}) + + {:else} +   + {/if} +
    +{/if} diff --git a/examples/svelte/playground/static/emblem-light.svg b/examples/svelte/playground/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/playground/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/playground/svelte.config.js b/examples/svelte/playground/svelte.config.js new file mode 100644 index 0000000000..a16d0de80a --- /dev/null +++ b/examples/svelte/playground/svelte.config.js @@ -0,0 +1,13 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/playground/tsconfig.json b/examples/svelte/playground/tsconfig.json new file mode 100644 index 0000000000..5c56cee332 --- /dev/null +++ b/examples/svelte/playground/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } +} diff --git a/examples/svelte/playground/vite.config.ts b/examples/svelte/playground/vite.config.ts new file mode 100644 index 0000000000..096c2069b7 --- /dev/null +++ b/examples/svelte/playground/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/simple/.gitignore b/examples/svelte/simple/.gitignore new file mode 100644 index 0000000000..59362fd3fa --- /dev/null +++ b/examples/svelte/simple/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +!lib/ + +# Editor directories and files +.vscode +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/svelte/simple/README.md b/examples/svelte/simple/README.md new file mode 100644 index 0000000000..4ef762ffec --- /dev/null +++ b/examples/svelte/simple/README.md @@ -0,0 +1,48 @@ +# Svelte + TS + Vite + +This template should help get you started developing with Svelte and TypeScript in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). + +## Need an official Svelte framework? + +Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. + +## Technical considerations + +**Why use this over SvelteKit?** + +- It brings its own routing solution which might not be preferable for some users. +- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. + `vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example. + +This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. + +Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. + +**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** + +Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. + +**Why include `.vscode/extensions.json`?** + +Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. + +**Why enable `allowJs` in the TS template?** + +While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant. + +**Why is HMR not preserving my local component state?** + +HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). + +If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. + +```ts +// store.ts +// An extremely simple external store +import { writable } from 'svelte/store' +export default writable(0) +``` diff --git a/examples/svelte/simple/index.html b/examples/svelte/simple/index.html new file mode 100644 index 0000000000..8fe436406e --- /dev/null +++ b/examples/svelte/simple/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Svelte + TS + + +
    + + + diff --git a/examples/svelte/simple/package.json b/examples/svelte/simple/package.json new file mode 100644 index 0000000000..c28f0ff202 --- /dev/null +++ b/examples/svelte/simple/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-simple", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.0.2", + "@tsconfig/svelte": "^3.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/simple/public/emblem-light.svg b/examples/svelte/simple/public/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/simple/public/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/simple/sandbox.config.json b/examples/svelte/simple/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/simple/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/simple/src/App.svelte b/examples/svelte/simple/src/App.svelte new file mode 100644 index 0000000000..76097f7e67 --- /dev/null +++ b/examples/svelte/simple/src/App.svelte @@ -0,0 +1,12 @@ + + + +
    + +
    +
    diff --git a/examples/svelte/simple/src/app.css b/examples/svelte/simple/src/app.css new file mode 100644 index 0000000000..bcc7233dd1 --- /dev/null +++ b/examples/svelte/simple/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/svelte/simple/src/assets/svelte.svg b/examples/svelte/simple/src/assets/svelte.svg new file mode 100644 index 0000000000..c5e08481f8 --- /dev/null +++ b/examples/svelte/simple/src/assets/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/svelte/simple/src/lib/Simple.svelte b/examples/svelte/simple/src/lib/Simple.svelte new file mode 100644 index 0000000000..7511be3a85 --- /dev/null +++ b/examples/svelte/simple/src/lib/Simple.svelte @@ -0,0 +1,41 @@ + + +

    Simple

    +
    +
    + {#if $query.isLoading} + Loading... + {/if} + {#if $query.error} + An error has occurred: + {$query.error.message} + {/if} + {#if $query.isSuccess} +
    +

    {$query.data.name}

    +

    {$query.data.description}

    + 👀 {$query.data.subscribers_count}{' '} + ✨ {$query.data.stargazers_count}{' '} + 🍴 {$query.data.forks_count} +
    + {/if} +
    +
    diff --git a/examples/svelte/simple/src/main.ts b/examples/svelte/simple/src/main.ts new file mode 100644 index 0000000000..8a909a15a0 --- /dev/null +++ b/examples/svelte/simple/src/main.ts @@ -0,0 +1,8 @@ +import './app.css' +import App from './App.svelte' + +const app = new App({ + target: document.getElementById('app'), +}) + +export default app diff --git a/examples/svelte/simple/src/vite-env.d.ts b/examples/svelte/simple/src/vite-env.d.ts new file mode 100644 index 0000000000..4078e7476a --- /dev/null +++ b/examples/svelte/simple/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/examples/svelte/simple/svelte.config.js b/examples/svelte/simple/svelte.config.js new file mode 100644 index 0000000000..49c64ac5bf --- /dev/null +++ b/examples/svelte/simple/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess() +} diff --git a/examples/svelte/simple/tsconfig.json b/examples/svelte/simple/tsconfig.json new file mode 100644 index 0000000000..d38303196a --- /dev/null +++ b/examples/svelte/simple/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + "baseUrl": ".", + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "isolatedModules": true + }, + "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/svelte/simple/tsconfig.node.json b/examples/svelte/simple/tsconfig.node.json new file mode 100644 index 0000000000..65dbdb96ae --- /dev/null +++ b/examples/svelte/simple/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node" + }, + "include": ["vite.config.ts"] +} diff --git a/examples/svelte/simple/vite.config.ts b/examples/svelte/simple/vite.config.ts new file mode 100644 index 0000000000..401b4d4bd6 --- /dev/null +++ b/examples/svelte/simple/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()] +}) diff --git a/examples/svelte/ssr/.gitignore b/examples/svelte/ssr/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/examples/svelte/ssr/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/examples/svelte/ssr/README.md b/examples/svelte/ssr/README.md new file mode 100644 index 0000000000..5c91169b0c --- /dev/null +++ b/examples/svelte/ssr/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/examples/svelte/ssr/package.json b/examples/svelte/ssr/package.json new file mode 100644 index 0000000000..80dbca3531 --- /dev/null +++ b/examples/svelte/ssr/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-svelte-ssr", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/ssr/sandbox.config.json b/examples/svelte/ssr/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/ssr/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/ssr/src/app.css b/examples/svelte/ssr/src/app.css new file mode 100644 index 0000000000..d301f1b2a3 --- /dev/null +++ b/examples/svelte/ssr/src/app.css @@ -0,0 +1,81 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +main { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +.button:hover { + border-color: #646cff; +} +.button:focus, +.button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + .button { + background-color: #f9f9f9; + } +} diff --git a/examples/svelte/ssr/src/app.d.ts b/examples/svelte/ssr/src/app.d.ts new file mode 100644 index 0000000000..3e4ed2057b --- /dev/null +++ b/examples/svelte/ssr/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Locals {} + // interface PageData {} + // interface Error {} + // interface Platform {} +} diff --git a/examples/svelte/ssr/src/app.html b/examples/svelte/ssr/src/app.html new file mode 100644 index 0000000000..bf205c1085 --- /dev/null +++ b/examples/svelte/ssr/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/ssr/src/lib/Post.svelte b/examples/svelte/ssr/src/lib/Post.svelte new file mode 100644 index 0000000000..7e077b7037 --- /dev/null +++ b/examples/svelte/ssr/src/lib/Post.svelte @@ -0,0 +1,31 @@ + + +
    +
    + Back +
    + {#if !postId || $post.isLoading} + Loading... + {/if} + {#if $post.error} + Error: {$post.error.message} + {/if} + {#if $post.isSuccess} +

    {$post.data.title}

    +
    +

    {$post.data.body}

    +
    +
    {$post.isFetching ? 'Background Updating...' : ' '}
    + {/if} +
    diff --git a/examples/svelte/ssr/src/lib/Posts.svelte b/examples/svelte/ssr/src/lib/Posts.svelte new file mode 100644 index 0000000000..83b4112f66 --- /dev/null +++ b/examples/svelte/ssr/src/lib/Posts.svelte @@ -0,0 +1,60 @@ + + +
    +
    + {#if $posts.status === 'loading'} + Loading... + {:else if $posts.status === 'error'} + Error: {$posts.error.message} + {:else} + + {#if $posts.isFetching} +
    + Background Updating... +
    + {/if} + {/if} +
    +
    + + diff --git a/examples/svelte/ssr/src/lib/data.ts b/examples/svelte/ssr/src/lib/data.ts new file mode 100644 index 0000000000..eed8ecbb1d --- /dev/null +++ b/examples/svelte/ssr/src/lib/data.ts @@ -0,0 +1,15 @@ +import type { Post } from './types' + +export const getPosts = async (limit: number) => { + const response = await fetch('https://jsonplaceholder.typicode.com/posts') + const data = (await response.json()) as Post[] + return data.filter((x) => x.id <= limit) +} + +export const getPostById = async (id: number): Promise => { + const response = await fetch( + `https://jsonplaceholder.typicode.com/posts/${id}`, + ) + const data = (await response.json()) as Post + return data +} diff --git a/examples/svelte/ssr/src/lib/types.ts b/examples/svelte/ssr/src/lib/types.ts new file mode 100644 index 0000000000..fb3835e9ff --- /dev/null +++ b/examples/svelte/ssr/src/lib/types.ts @@ -0,0 +1,5 @@ +export type Post = { + id: number + title: string + body: string +} diff --git a/examples/svelte/ssr/src/routes/+layout.svelte b/examples/svelte/ssr/src/routes/+layout.svelte new file mode 100644 index 0000000000..17e4438650 --- /dev/null +++ b/examples/svelte/ssr/src/routes/+layout.svelte @@ -0,0 +1,13 @@ + + + +
    + +
    +
    diff --git a/examples/svelte/ssr/src/routes/+layout.ts b/examples/svelte/ssr/src/routes/+layout.ts new file mode 100644 index 0000000000..53b5e7ec76 --- /dev/null +++ b/examples/svelte/ssr/src/routes/+layout.ts @@ -0,0 +1,15 @@ +import { browser } from '$app/environment' +import { QueryClient } from '@tanstack/svelte-query' +import type { LayoutLoad } from './$types' + +export const load: LayoutLoad = async () => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + enabled: browser, + }, + }, + }) + + return { queryClient } +} diff --git a/examples/svelte/ssr/src/routes/+page.svelte b/examples/svelte/ssr/src/routes/+page.svelte new file mode 100644 index 0000000000..dc95af5a51 --- /dev/null +++ b/examples/svelte/ssr/src/routes/+page.svelte @@ -0,0 +1,6 @@ + + +

    Basic Query with SSR

    + diff --git a/examples/svelte/ssr/src/routes/+page.ts b/examples/svelte/ssr/src/routes/+page.ts new file mode 100644 index 0000000000..021b90ca9f --- /dev/null +++ b/examples/svelte/ssr/src/routes/+page.ts @@ -0,0 +1,11 @@ +import { getPosts } from '$lib/data' +import type { PageLoad } from './$types' + +export const load: PageLoad = async ({ parent }) => { + const { queryClient } = await parent() + + await queryClient.prefetchQuery({ + queryKey: ['posts', 10], + queryFn: () => getPosts(10), + }) +} diff --git a/examples/svelte/ssr/src/routes/[postId]/+page.svelte b/examples/svelte/ssr/src/routes/[postId]/+page.svelte new file mode 100644 index 0000000000..b68acc0bc0 --- /dev/null +++ b/examples/svelte/ssr/src/routes/[postId]/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/examples/svelte/ssr/src/routes/[postId]/+page.ts b/examples/svelte/ssr/src/routes/[postId]/+page.ts new file mode 100644 index 0000000000..b30a7de223 --- /dev/null +++ b/examples/svelte/ssr/src/routes/[postId]/+page.ts @@ -0,0 +1,15 @@ +import { getPostById } from '$lib/data' +import type { PageLoad } from './$types' + +export const load: PageLoad = async ({ parent, params }) => { + const { queryClient } = await parent() + + const postId = parseInt(params.postId) + + await queryClient.prefetchQuery({ + queryKey: ['post', postId], + queryFn: () => getPostById(postId), + }) + + return { postId } +} diff --git a/examples/svelte/ssr/static/emblem-light.svg b/examples/svelte/ssr/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/ssr/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/ssr/svelte.config.js b/examples/svelte/ssr/svelte.config.js new file mode 100644 index 0000000000..836e30422d --- /dev/null +++ b/examples/svelte/ssr/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/ssr/tsconfig.json b/examples/svelte/ssr/tsconfig.json new file mode 100644 index 0000000000..794b95b642 --- /dev/null +++ b/examples/svelte/ssr/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/examples/svelte/ssr/vite.config.ts b/examples/svelte/ssr/vite.config.ts new file mode 100644 index 0000000000..f2eb6d93cf --- /dev/null +++ b/examples/svelte/ssr/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/svelte/star-wars/.gitignore b/examples/svelte/star-wars/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/examples/svelte/star-wars/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/examples/svelte/star-wars/README.md b/examples/svelte/star-wars/README.md new file mode 100644 index 0000000000..b5f81c13d5 --- /dev/null +++ b/examples/svelte/star-wars/README.md @@ -0,0 +1,8 @@ +# Example + +This example is a rewrite of the React Query star-wars example. + +To run this example: + +- `npm install` +- `npm run dev` diff --git a/examples/svelte/star-wars/package.json b/examples/svelte/star-wars/package.json new file mode 100644 index 0000000000..22afddea1d --- /dev/null +++ b/examples/svelte/star-wars/package.json @@ -0,0 +1,27 @@ +{ + "name": "@tanstack/query-example-svelte-star-wars", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@tanstack/svelte-query": "^4.20.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^1.0.0", + "@sveltejs/kit": "^1.0.0", + "autoprefixer": "^10.4.13", + "postcss": "^8.4.20", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tailwindcss": "^3.2.4", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0" + } +} diff --git a/examples/svelte/star-wars/postcss.config.cjs b/examples/svelte/star-wars/postcss.config.cjs new file mode 100644 index 0000000000..116848f66a --- /dev/null +++ b/examples/svelte/star-wars/postcss.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + plugins: [require("tailwindcss"), require("autoprefixer")], +}; diff --git a/examples/svelte/star-wars/sandbox.config.json b/examples/svelte/star-wars/sandbox.config.json new file mode 100644 index 0000000000..0da04c0cad --- /dev/null +++ b/examples/svelte/star-wars/sandbox.config.json @@ -0,0 +1,5 @@ +{ + "container": { + "node": "16" + } +} diff --git a/examples/svelte/star-wars/src/app.css b/examples/svelte/star-wars/src/app.css new file mode 100644 index 0000000000..317ab3f2d1 --- /dev/null +++ b/examples/svelte/star-wars/src/app.css @@ -0,0 +1,18 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply font-sans; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + @apply font-mono; + } +} diff --git a/examples/svelte/star-wars/src/app.d.ts b/examples/svelte/star-wars/src/app.d.ts new file mode 100644 index 0000000000..1cea0dcf2b --- /dev/null +++ b/examples/svelte/star-wars/src/app.d.ts @@ -0,0 +1,9 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} +} diff --git a/examples/svelte/star-wars/src/app.html b/examples/svelte/star-wars/src/app.html new file mode 100644 index 0000000000..d847151a12 --- /dev/null +++ b/examples/svelte/star-wars/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
    %sveltekit.body%
    + + diff --git a/examples/svelte/star-wars/src/routes/+layout.svelte b/examples/svelte/star-wars/src/routes/+layout.svelte new file mode 100644 index 0000000000..36d59c23a8 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/+layout.svelte @@ -0,0 +1,28 @@ + + + + Svelte Query Star Wars Example + + + + + + diff --git a/examples/svelte/star-wars/src/routes/+page.svelte b/examples/svelte/star-wars/src/routes/+page.svelte new file mode 100644 index 0000000000..eaaf33aa03 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/+page.svelte @@ -0,0 +1,32 @@ +
    +

    React Query Demo

    +

    Using the Star Wars API

    +

    + (Built by @Brent_m_Clark + ) +

    +
    +
    Why React Query?
    +

    + In this demo you will be able to see how React Query is a significant + improvement over redux, mobx, and any + other general-purpose state container. +

    +

    + No reducers, thunks, or sagas. No ES6 models to maintain in order to tag + them as observable. +

    +

    + Simply associate a key with your fetch call and let{' '} + React Query handle the rest. +

    +
    Ready to get started?
    +

    + Check out the{' '} + Films + and + Characters + ! +

    +
    +
    diff --git a/examples/svelte/star-wars/src/routes/characters/+page.svelte b/examples/svelte/star-wars/src/routes/characters/+page.svelte new file mode 100644 index 0000000000..77f868d693 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/characters/+page.svelte @@ -0,0 +1,36 @@ + + +{#if $query.status === 'loading'} +

    Loading...

    +{/if} + +{#if $query.status === 'error'} +

    Error :(

    +{/if} + +{#if $query.status === 'success'} +
    +

    Characters

    + {#each $query.data.results as person} + {@const personUrlParts = person.url.split('/').filter(Boolean)} + {@const personId = personUrlParts[personUrlParts.length - 1]} + + {/each} +
    +{/if} diff --git a/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.svelte b/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.svelte new file mode 100644 index 0000000000..624ee7dece --- /dev/null +++ b/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.svelte @@ -0,0 +1,77 @@ + + +{#if $query.status === 'loading'} +

    Loading...

    +{/if} + +{#if $query.status === 'error'} +

    Error :(

    +{/if} + +{#if $query.status === 'success'} + {@const homeworldUrlParts = $query.data.homeworld.split('/').filter(Boolean)} + {@const homeworldId = homeworldUrlParts[homeworldUrlParts.length - 1]} +
    +

    {$query.data.name}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FeatureValue
    Born{$query.data.birth_year}
    Eyes{$query.data.eye_color}
    Hair{$query.data.hair_color}
    Height{$query.data.height}
    Mass{$query.data.mass}
    Homeworld
    +
    +

    Films

    + {#each $query.data.films as film} + {@const filmUrlParts = film.split('/').filter(Boolean)} + {@const filmId = filmUrlParts[filmUrlParts.length - 1]} + + {/each} +
    +{/if} diff --git a/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.ts b/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.ts new file mode 100644 index 0000000000..dbfde8eb56 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/characters/[characterId]/+page.ts @@ -0,0 +1,5 @@ +import type { PageLoad } from './$types' + +export const load: PageLoad = ({ params }) => { + return { params } +} diff --git a/examples/svelte/star-wars/src/routes/characters/[characterId]/Film.svelte b/examples/svelte/star-wars/src/routes/characters/[characterId]/Film.svelte new file mode 100644 index 0000000000..7c0210d8d5 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/characters/[characterId]/Film.svelte @@ -0,0 +1,23 @@ + + +{#if $query.status === 'success'} + +{/if} diff --git a/examples/svelte/star-wars/src/routes/characters/[characterId]/Homeworld.svelte b/examples/svelte/star-wars/src/routes/characters/[characterId]/Homeworld.svelte new file mode 100644 index 0000000000..d931b8cc19 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/characters/[characterId]/Homeworld.svelte @@ -0,0 +1,21 @@ + + +{#if $query.status === 'success'} + + {$query.data.name} + +{/if} diff --git a/examples/svelte/star-wars/src/routes/films/+page.svelte b/examples/svelte/star-wars/src/routes/films/+page.svelte new file mode 100644 index 0000000000..3a7cb24713 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/films/+page.svelte @@ -0,0 +1,41 @@ + + +{#if $query.status === 'loading'} +

    Loading...

    +{/if} + +{#if $query.status === 'error'} +

    Error :(

    +{/if} + +{#if $query.status === 'success'} +
    +

    Films

    + {#each $query.data.results as film} + {@const filmUrlParts = film.url.split('/').filter(Boolean)} + {@const filmId = filmUrlParts[filmUrlParts.length - 1]} + + {/each} +
    +{/if} diff --git a/examples/svelte/star-wars/src/routes/films/[filmId]/+page.svelte b/examples/svelte/star-wars/src/routes/films/[filmId]/+page.svelte new file mode 100644 index 0000000000..988ccc6ad5 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/films/[filmId]/+page.svelte @@ -0,0 +1,41 @@ + + +{#if $query.status === 'loading'} +

    Loading...

    +{/if} + +{#if $query.status === 'error'} +

    Error :(

    +{/if} + +{#if $query.status === 'success'} +
    +

    {$query.data.title}

    +

    {$query.data.opening_crawl}

    +
    +

    Characters

    + {#each $query.data.characters as character} + {@const characterUrlParts = character.split('/').filter(Boolean)} + {@const characterId = characterUrlParts[characterUrlParts.length - 1]} + + {/each} +
    +{/if} diff --git a/examples/svelte/star-wars/src/routes/films/[filmId]/+page.ts b/examples/svelte/star-wars/src/routes/films/[filmId]/+page.ts new file mode 100644 index 0000000000..dbfde8eb56 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/films/[filmId]/+page.ts @@ -0,0 +1,5 @@ +import type { PageLoad } from './$types' + +export const load: PageLoad = ({ params }) => { + return { params } +} diff --git a/examples/svelte/star-wars/src/routes/films/[filmId]/Character.svelte b/examples/svelte/star-wars/src/routes/films/[filmId]/Character.svelte new file mode 100644 index 0000000000..b4827ccdf9 --- /dev/null +++ b/examples/svelte/star-wars/src/routes/films/[filmId]/Character.svelte @@ -0,0 +1,23 @@ + + +{#if $query.status === 'success'} + +{/if} diff --git a/examples/svelte/star-wars/static/emblem-light.svg b/examples/svelte/star-wars/static/emblem-light.svg new file mode 100644 index 0000000000..a58e69ad5e --- /dev/null +++ b/examples/svelte/star-wars/static/emblem-light.svg @@ -0,0 +1,13 @@ + + + + emblem-light + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/examples/svelte/star-wars/svelte.config.js b/examples/svelte/star-wars/svelte.config.js new file mode 100644 index 0000000000..a16d0de80a --- /dev/null +++ b/examples/svelte/star-wars/svelte.config.js @@ -0,0 +1,13 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/examples/svelte/star-wars/tailwind.config.cjs b/examples/svelte/star-wars/tailwind.config.cjs new file mode 100644 index 0000000000..fdcf583098 --- /dev/null +++ b/examples/svelte/star-wars/tailwind.config.cjs @@ -0,0 +1,11 @@ +module.exports = { + content: ["./src/**/*.{html,js,svelte,ts}"], + theme: { + fontFamily: { + sans: + "Roboto, BlinkMacSystemFont, -apple-system, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, Helvetica, Arial, sans-serif", + mono: + "Roboto Mono, BlinkMacSystemFont, -apple-system, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, Helvetica, Arial, sans-serif", + }, + }, +}; diff --git a/examples/svelte/star-wars/tsconfig.json b/examples/svelte/star-wars/tsconfig.json new file mode 100644 index 0000000000..5c56cee332 --- /dev/null +++ b/examples/svelte/star-wars/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } +} diff --git a/examples/svelte/star-wars/vite.config.ts b/examples/svelte/star-wars/vite.config.ts new file mode 100644 index 0000000000..096c2069b7 --- /dev/null +++ b/examples/svelte/star-wars/vite.config.ts @@ -0,0 +1,8 @@ +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + plugins: [sveltekit()] +}; + +export default config; diff --git a/examples/vue/persister/package.json b/examples/vue/persister/package.json index cd79cb921b..c16bbf413c 100644 --- a/examples/vue/persister/package.json +++ b/examples/vue/persister/package.json @@ -1,5 +1,5 @@ { - "name": "@tanstack/query-example-vue-basic", + "name": "@tanstack/query-example-vue-persister", "private": true, "scripts": { "dev": "vite", diff --git a/package.json b/package.json index b5022b4927..abed8abd25 100644 --- a/package.json +++ b/package.json @@ -2,21 +2,23 @@ "name": "query", "repository": "https://github.com/tanstack/query.git", "scripts": { - "clean": "pnpm --filter \"./packages/**\" --parallel --no-bail run clean", + "clean": "pnpm --filter \"./packages/**\" run clean", "preinstall": "node -e \"if(process.env.CI == 'true') {console.log('Skipping preinstall...'); process.exit(1)}\" || npx -y only-allow pnpm", "install:csb": "pnpm install --frozen-lockfile", "test": "pnpm run test:ci", - "test:ci": "pnpm run test:format && pnpm run test:eslint && pnpm run test:jest --collectCoverage false && pnpm run typecheck", - "test:eslint": "pnpm --filter \"./packages/**\" --parallel --no-bail run test:eslint", + "test:ci": "pnpm run test:format && pnpm run test:eslint && pnpm run test:lib:publish && pnpm run test:types", + "test:eslint": "pnpm --filter \"./packages/**\" run test:eslint", "test:format": "pnpm run prettier --check", - "test:jest": "pnpm --filter \"./packages/**\" --parallel --no-bail run test:jest", + "test:lib": "pnpm --filter \"./packages/**\" run test:lib", + "test:lib:dev": "pnpm --filter \"./packages/**\" run test:lib:dev", + "test:lib:publish": "pnpm --filter \"./packages/**\" run test:lib:publish", "test:size": "pnpm run build && bundlewatch", - "build": "rollup --config rollup.config.js && pnpm run typecheck && pnpm pnpm --filter \"eslint-plugin-query\" run build && pnpm run build:copyTypes", - "build:copyTypes": "cp packages/react-query-devtools/build/lib/index.d.ts packages/react-query-devtools/build/lib/index.prod.d.ts", - "typecheck": "tsc -b", - "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"pnpm run typecheck --watch\"", + "test:types": "pnpm --filter \"./packages/**\" run test:types", + "build": "rollup --config rollup.config.js && pnpm --filter \"./packages/**\" run build && pnpm run build:types", + "build:types": "pnpm --filter \"./packages/**\" run build:types", + "watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"pnpm run build:types --watch\"", "dev": "pnpm run watch", - "prettier": "prettier \"{packages,examples}/**/src/**/*.{md,js,jsx,ts,tsx,json,vue}\"", + "prettier": "prettier --plugin-search-dir . \"{packages,examples}/**/src/**/*.{md,js,jsx,ts,tsx,json,vue,svelte}\"", "prettier:write": "pnpm run prettier --write", "cipublish": "ts-node scripts/publish.ts" }, @@ -35,7 +37,6 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", "@testing-library/react-hooks": "^7.0.2", - "@tsconfig/svelte": "^3.0.0", "@types/jest": "^26.0.4", "@types/luxon": "^2.3.1", "@types/node": "^17.0.25", @@ -72,17 +73,17 @@ "jsonfile": "^6.1.0", "luxon": "^2.3.2", "prettier": "^2.6.2", + "prettier-plugin-svelte": "^2.9.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "rimraf": "^3.0.2", "rollup": "^2.70.2", "rollup-plugin-size": "^0.2.2", - "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-visualizer": "^5.6.0", "solid-js": "^1.5.7", "solid-testing-library": "^0.3.0", "stream-to-array": "^2.3.0", - "svelte": "^3.48.0", "ts-jest": "^27.1.1", "ts-node": "^10.7.0", "typescript": "4.7.4", diff --git a/packages/eslint-plugin-query/package.json b/packages/eslint-plugin-query/package.json index 8f30f0d3cd..5b1b39436f 100644 --- a/packages/eslint-plugin-query/package.json +++ b/packages/eslint-plugin-query/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/eslint-plugin-query", - "version": "4.20.10", + "version": "4.21.0", "description": "ESLint plugin for TanStack Query", "author": "Eliya Cohen", "license": "MIT", @@ -12,13 +12,14 @@ }, "main": "build/lib/index.js", "scripts": { - "typecheck": "tsc", - "build": "tsup --minify", + "clean": "rimraf ./build", "dev": "tsup --watch --sourcemap", - "clean": "rm -rf ./build", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:dev": "pnpm run test:jest --watch" + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build": "tsup --minify" }, "files": [ "build" diff --git a/packages/query-async-storage-persister/package.json b/packages/query-async-storage-persister/package.json index ad269ab1a1..d6dd8f520f 100644 --- a/packages/query-async-storage-persister/package.json +++ b/packages/query-async-storage-persister/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/query-async-storage-persister", - "version": "4.20.9", + "version": "4.22.4", "description": "A persister for asynchronous storages, to be used with TanStack/Query", "author": "tannerlinsley", "license": "MIT", @@ -28,10 +28,13 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "dependencies": { "@tanstack/query-persist-client-core": "workspace:*" diff --git a/packages/query-broadcast-client-experimental/jest.config.ts b/packages/query-broadcast-client-experimental/jest.config.ts deleted file mode 100644 index 76da265ace..0000000000 --- a/packages/query-broadcast-client-experimental/jest.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default { - displayName: 'query-broadcast-client-experimental', - preset: '../../jest-preset.js', -} diff --git a/packages/query-broadcast-client-experimental/package.json b/packages/query-broadcast-client-experimental/package.json index 1d23f0bf4d..e2e079212d 100644 --- a/packages/query-broadcast-client-experimental/package.json +++ b/packages/query-broadcast-client-experimental/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/query-broadcast-client-experimental", - "version": "4.20.9", + "version": "4.22.4", "description": "An experimental plugin to for broadcasting the state of your queryClient between browser tabs/windows", "author": "tannerlinsley", "license": "MIT", @@ -28,10 +28,10 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts --passWithNoTests", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "build:types": "tsc --build" }, "dependencies": { "@tanstack/query-core": "workspace:*", diff --git a/packages/query-core/package.json b/packages/query-core/package.json index 2bb4730065..30dcf50de2 100644 --- a/packages/query-core/package.json +++ b/packages/query-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/query-core", - "version": "4.20.9", + "version": "4.22.4", "description": "The framework agnostic core that powers TanStack Query", "author": "tannerlinsley", "license": "MIT", @@ -28,9 +28,12 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" } } diff --git a/packages/query-core/src/hydration.ts b/packages/query-core/src/hydration.ts index c44dd64de2..b99f507997 100644 --- a/packages/query-core/src/hydration.ts +++ b/packages/query-core/src/hydration.ts @@ -65,11 +65,11 @@ function dehydrateQuery(query: Query): DehydratedQuery { } } -function defaultShouldDehydrateMutation(mutation: Mutation) { +export function defaultShouldDehydrateMutation(mutation: Mutation) { return mutation.state.isPaused } -function defaultShouldDehydrateQuery(query: Query) { +export function defaultShouldDehydrateQuery(query: Query) { return query.state.status === 'success' } diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index ede848cdd1..1953ff9d2b 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -14,7 +14,12 @@ export { onlineManager } from './onlineManager' export { hashKey, replaceEqualDeep, isServer, keepPreviousData } from './utils' export type { MutationFilters, QueryFilters, Updater } from './utils' export { isCancelledError } from './retryer' -export { dehydrate, hydrate } from './hydration' +export { + dehydrate, + hydrate, + defaultShouldDehydrateMutation, + defaultShouldDehydrateQuery, +} from './hydration' // Types export * from './types' diff --git a/packages/query-core/src/mutationObserver.ts b/packages/query-core/src/mutationObserver.ts index f6156b461b..f4b0c700b2 100644 --- a/packages/query-core/src/mutationObserver.ts +++ b/packages/query-core/src/mutationObserver.ts @@ -161,7 +161,7 @@ export class MutationObserver< #notify(options: NotifyOptions) { notifyManager.batch(() => { // First trigger the mutate callbacks - if (this.#mutateOptions) { + if (this.#mutateOptions && this.hasListeners()) { if (options.onSuccess) { this.#mutateOptions.onSuccess?.( this.#currentResult.data!, diff --git a/packages/query-core/src/tests/mutations.test.tsx b/packages/query-core/src/tests/mutations.test.tsx index aa8fa03698..9d699fdb26 100644 --- a/packages/query-core/src/tests/mutations.test.tsx +++ b/packages/query-core/src/tests/mutations.test.tsx @@ -342,4 +342,20 @@ describe('mutations', () => { } expect(error).toEqual(new Error('No mutationFn found')) }) + + test('mutate update the mutation state even without an active subscription', async () => { + const onSuccess = jest.fn() + const onSettled = jest.fn() + + const mutation = new MutationObserver(queryClient, { + mutationFn: async () => { + return 'update' + }, + }) + + await mutation.mutate(undefined, { onSuccess, onSettled }) + expect(mutation.getCurrentResult().data).toEqual('update') + expect(onSuccess).not.toHaveBeenCalled() + expect(onSettled).not.toHaveBeenCalled() + }) }) diff --git a/packages/query-persist-client-core/package.json b/packages/query-persist-client-core/package.json index f38dc830b9..997e989676 100644 --- a/packages/query-persist-client-core/package.json +++ b/packages/query-persist-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/query-persist-client-core", - "version": "4.20.9", + "version": "4.22.4", "description": "Set of utilities for interacting with persisters, which can save your queryClient for later use", "author": "tannerlinsley", "license": "MIT", @@ -28,10 +28,13 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "devDependencies": { "@tanstack/query-core": "workspace:*" diff --git a/packages/query-sync-storage-persister/package.json b/packages/query-sync-storage-persister/package.json index 7394a3c646..ad2a80cacf 100644 --- a/packages/query-sync-storage-persister/package.json +++ b/packages/query-sync-storage-persister/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/query-sync-storage-persister", - "version": "4.20.9", + "version": "4.22.4", "description": "A persister for synchronous storages, to be used with TanStack/Query", "author": "tannerlinsley", "license": "MIT", @@ -28,10 +28,13 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "dependencies": { "@tanstack/query-persist-client-core": "workspace:*" diff --git a/packages/react-query-devtools/package.json b/packages/react-query-devtools/package.json index 2c7c553c40..00bf04229d 100644 --- a/packages/react-query-devtools/package.json +++ b/packages/react-query-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-query-devtools", - "version": "4.20.9", + "version": "4.22.4", "description": "Developer tools to interact with and visualize the TanStack/react-query cache", "author": "tannerlinsley", "license": "MIT", @@ -38,10 +38,13 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build && cp build/lib/index.d.ts build/lib/index.prod.d.ts" }, "devDependencies": { "@tanstack/react-query": "workspace:*", diff --git a/packages/react-query-persist-client/package.json b/packages/react-query-persist-client/package.json index 171eb07f52..5cdf8a0c4c 100644 --- a/packages/react-query-persist-client/package.json +++ b/packages/react-query-persist-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-query-persist-client", - "version": "4.20.9", + "version": "4.22.4", "description": "React bindings to work with persisters in TanStack/react-query", "author": "tannerlinsley", "license": "MIT", @@ -28,10 +28,13 @@ "src" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "devDependencies": { "@tanstack/react-query": "workspace:*", diff --git a/packages/react-query/README.md b/packages/react-query/README.md new file mode 100644 index 0000000000..f98d650d8b --- /dev/null +++ b/packages/react-query/README.md @@ -0,0 +1,51 @@ + + +![TanStack Query Header](https://github.com/TanStack/query/raw/beta/media/repo-header.png) + +Hooks for fetching, caching and updating asynchronous data in React + + + #TanStack + + + + + + + + + + semantic-release + + Join the discussion on Github +Best of JS + + + + + Gitpod Ready-to-Code + + +Enjoy this library? Try the entire [TanStack](https://tanstack.com)! [TanStack Table](https://github.com/TanStack/table), [TanStack Router](https://github.com/tanstack/router), [TanStack Virtual](https://github.com/tanstack/virtual), [React Charts](https://github.com/TanStack/react-charts), [React Ranger](https://github.com/TanStack/ranger) + +## Visit [tanstack.com/query](https://tanstack.com/query) for docs, guides, API and more! + +## Quick Features + +- Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!) +- Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime) +- Parallel + Dependent Queries +- Mutations + Reactive Query Refetching +- Multi-layer Cache + Automatic Garbage Collection +- Paginated + Cursor-based Queries +- Load-More + Infinite Scroll Queries w/ Scroll Recovery +- Request Cancellation +- [React Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html) + Fetch-As-You-Render Query Prefetching +- Dedicated Devtools +- + + (depending on features imported) + +### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/) + + diff --git a/packages/react-query/codemods/jest.config.js b/packages/react-query/codemods/jest.config.js deleted file mode 100644 index 5b36187188..0000000000 --- a/packages/react-query/codemods/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - testMatch: ['/**/*.test.js'], -} diff --git a/packages/react-query/jest.config.ts b/packages/react-query/jest.config.ts index fbe2267f71..f4a5b3fedd 100644 --- a/packages/react-query/jest.config.ts +++ b/packages/react-query/jest.config.ts @@ -2,4 +2,5 @@ export default { displayName: 'react-query', preset: '../../jest-preset.js', setupFilesAfterEnv: ['./jest.setup.ts'], + testMatch: ['/src/**/*.test.tsx', '/codemods/**/*.test.js'], } diff --git a/packages/react-query/package.json b/packages/react-query/package.json index ba9c3eef3f..b272991f72 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-query", - "version": "4.20.9", + "version": "4.22.4", "description": "Hooks for managing, caching and syncing asynchronous and remote data in React", "author": "tannerlinsley", "license": "MIT", @@ -23,11 +23,13 @@ }, "sideEffects": false, "scripts": { - "clean": "rm -rf ./build", - "test:codemods": "../../node_modules/.bin/jest --config codemods/jest.config.js", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "pnpm run test:codemods && ../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "files": [ "build/lib/*", diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 3b7a1b53a5..aa372a0163 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -1057,7 +1057,7 @@ describe('useQueries with suspense', () => { queryKey: key1, queryFn: async () => { results.push('1') - await sleep(10) + await sleep(50) return '1' }, suspense: true, @@ -1066,7 +1066,7 @@ describe('useQueries with suspense', () => { queryKey: key2, queryFn: async () => { results.push('2') - await sleep(20) + await sleep(200) return '2' }, staleTime: 1000, @@ -1074,6 +1074,7 @@ describe('useQueries with suspense', () => { }, ], }) + return (

    data: {result.map((it) => it.data ?? 'null').join(',')}

    diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx index 1de959f3c8..a224e93f27 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test.tsx @@ -275,12 +275,15 @@ describe('useInfiniteQuery', () => { }), }) states.push(state) - return null + + return
    {state.data?.pages.join(',')}
    } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await sleep(10) + await waitFor(() => { + rendered.getByText('count: 1') + }) expect(states.length).toBe(2) expect(states[0]).toMatchObject({ @@ -311,12 +314,21 @@ describe('useInfiniteQuery', () => { }, []), }) states.push(state) - return null + + return ( +
    + {state.data?.pages.map((page) => ( +
    count: {page.count}
    + ))} +
    + ) } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await sleep(20) + await waitFor(() => { + rendered.getByText('count: 1') + }) expect(states.length).toBe(2) expect(selectCalled).toBe(1) @@ -405,20 +417,29 @@ describe('useInfiniteQuery', () => { states.push(state) - const { fetchPreviousPage } = state + return ( +
    +
    data: {state.data?.pages.join(',') ?? null}
    + +
    + ) + } - React.useEffect(() => { - setActTimeout(() => { - fetchPreviousPage() - }, 20) - }, [fetchPreviousPage]) + const rendered = renderWithClient(queryClient, ) - return null - } + await waitFor(() => { + rendered.getByText('data: 10') + }) - renderWithClient(queryClient, ) + fireEvent.click( + rendered.getByRole('button', { name: /fetch previous page/i }), + ) - await sleep(100) + await waitFor(() => { + rendered.getByText('data: 9,10') + }) expect(states.length).toBe(4) expect(states[0]).toMatchObject({ diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 20c7f8b331..cb07a22040 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -966,7 +966,7 @@ describe('useMutation', () => { ) }) - test('should go to error state if onSuccess callback errors', async () => { + it('should go to error state if onSuccess callback errors', async () => { const error = new Error('error from onSuccess') const onError = jest.fn() @@ -999,7 +999,7 @@ describe('useMutation', () => { expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) }) - test('should go to error state if onError callback errors', async () => { + it('should go to error state if onError callback errors', async () => { const error = new Error('error from onError') const mutateFnError = new Error('mutateFnError') @@ -1033,7 +1033,7 @@ describe('useMutation', () => { await rendered.findByText('error: mutateFnError, status: error') }) - test('should go to error state if onSettled callback errors', async () => { + it('should go to error state if onSettled callback errors', async () => { const error = new Error('error from onSettled') const mutateFnError = new Error('mutateFnError') const onError = jest.fn() diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 7630454c66..9bd4b01dac 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -44,7 +44,7 @@ describe('useQueries', () => { { queryKey: key2, queryFn: async () => { - await sleep(100) + await sleep(200) return 2 }, }, diff --git a/packages/solid-query/package.json b/packages/solid-query/package.json index f806fc6307..ccb59f4f15 100644 --- a/packages/solid-query/package.json +++ b/packages/solid-query/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-query", - "version": "4.20.9", + "version": "4.22.4", "description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid", "author": "tannerlinsley", "license": "MIT", @@ -29,10 +29,13 @@ "./src/setBatchUpdatesFn.ts" ], "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "files": [ "build/lib/*", diff --git a/packages/svelte-query/.eslintrc b/packages/svelte-query/.eslintrc new file mode 100644 index 0000000000..cc8b7118d3 --- /dev/null +++ b/packages/svelte-query/.eslintrc @@ -0,0 +1,25 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module", + "extraFileExtensions": [".svelte"] + }, + "rules": { + "react-hooks/rules-of-hooks": "off" + }, + "extends": [ + "plugin:svelte/recommended", + "../../.eslintrc" + ], + "ignorePatterns": ["*.config.*", "**/build/*", "**/.svelte-kit/*"], + "overrides": [ + { + "files": ["*.svelte"], + "parser": "svelte-eslint-parser", + "parserOptions": { + "parser": "@typescript-eslint/parser" + } + } + ] +} diff --git a/packages/svelte-query/.gitignore b/packages/svelte-query/.gitignore new file mode 100644 index 0000000000..6fd24c7d48 --- /dev/null +++ b/packages/svelte-query/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +!lib/ diff --git a/packages/svelte-query/package.json b/packages/svelte-query/package.json new file mode 100644 index 0000000000..3b75811634 --- /dev/null +++ b/packages/svelte-query/package.json @@ -0,0 +1,54 @@ +{ + "name": "@tanstack/svelte-query", + "version": "4.22.4", + "description": "Primitives for managing, caching and syncing asynchronous and remote data in Svelte", + "author": "Dre Johnson", + "license": "MIT", + "repository": "tanstack/query", + "homepage": "https://tanstack.com/query", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "types": "build/lib/index.d.ts", + "module": "build/lib/index.js", + "type": "module", + "scripts": { + "clean": "rimraf ./build", + "test:types": "svelte-check --tsconfig ./tsconfig.json", + "test:eslint": "eslint --ext .svelte,.ts ./src", + "test:lib": "vitest run --coverage true", + "test:lib:dev": "vitest watch", + "test:lib:publish": "vitest run --coverage false", + "build": "svelte-package && rimraf ./build/lib/package.json" + }, + "devDependencies": { + "@sveltejs/package": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^2.0.2", + "@testing-library/svelte": "^3.2.2", + "@vitest/coverage-istanbul": "^0.27.1", + "eslint-plugin-svelte": "^2.14.1", + "jsdom": "^20.0.3", + "svelte": "^3.54.0", + "svelte-check": "^2.9.2", + "tslib": "^2.4.1", + "typescript": "^4.7.4", + "vite": "^4.0.0", + "vitest": "^0.27.1" + }, + "dependencies": { + "@tanstack/query-core": "workspace:*" + }, + "peerDependencies": { + "svelte": "^3.54.0" + }, + "exports": { + "./package.json": "./package.json", + ".": "./build/lib/index.js" + }, + "svelte": "./build/lib/index.js", + "files": [ + "build/lib/*", + "src" + ] +} diff --git a/packages/svelte-query/src/HydrationBoundary.svelte b/packages/svelte-query/src/HydrationBoundary.svelte new file mode 100644 index 0000000000..2d106da00e --- /dev/null +++ b/packages/svelte-query/src/HydrationBoundary.svelte @@ -0,0 +1,16 @@ + + + diff --git a/packages/svelte-query/src/QueryClientProvider.svelte b/packages/svelte-query/src/QueryClientProvider.svelte new file mode 100644 index 0000000000..86ce96ecea --- /dev/null +++ b/packages/svelte-query/src/QueryClientProvider.svelte @@ -0,0 +1,19 @@ + + + diff --git a/packages/svelte-query/src/__tests__/CreateMutation.svelte b/packages/svelte-query/src/__tests__/CreateMutation.svelte new file mode 100644 index 0000000000..42d4d06f5d --- /dev/null +++ b/packages/svelte-query/src/__tests__/CreateMutation.svelte @@ -0,0 +1,15 @@ + + + diff --git a/packages/svelte-query/src/__tests__/CreateQuery.svelte b/packages/svelte-query/src/__tests__/CreateQuery.svelte new file mode 100644 index 0000000000..6e12574d16 --- /dev/null +++ b/packages/svelte-query/src/__tests__/CreateQuery.svelte @@ -0,0 +1,23 @@ + + +{#if $query.isLoading} +

    Loading

    +{:else if $query.isError} +

    Error

    +{:else if $query.isSuccess} +

    Success

    +{/if} diff --git a/packages/svelte-query/src/__tests__/createMutation.test.ts b/packages/svelte-query/src/__tests__/createMutation.test.ts new file mode 100644 index 0000000000..bfa64be0b8 --- /dev/null +++ b/packages/svelte-query/src/__tests__/createMutation.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect, vi } from 'vitest' +import { fireEvent, render, screen } from '@testing-library/svelte' +import CreateMutation from './CreateMutation.svelte' +import { sleep } from './utils' + +describe('createMutation', () => { + it('Call mutate and check function runs', async () => { + const mutationFn = vi.fn() + + render(CreateMutation, { + props: { + mutationFn, + }, + }) + + fireEvent.click(screen.getByRole('button')) + + await sleep(200) + + expect(mutationFn).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts new file mode 100644 index 0000000000..191fea1b23 --- /dev/null +++ b/packages/svelte-query/src/__tests__/createQuery.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/svelte' +import CreateQuery from './CreateQuery.svelte' +import { sleep } from './utils' + +describe('createQuery', () => { + it('Render and wait for success', async () => { + render(CreateQuery, { + props: { + queryKey: ['test'], + queryFn: async () => { + await sleep(100) + return 'Success' + }, + }, + }) + + expect(screen.queryByText('Loading')).toBeInTheDocument() + expect(screen.queryByText('Error')).not.toBeInTheDocument() + expect(screen.queryByText('Success')).not.toBeInTheDocument() + + await sleep(200) + + expect(screen.queryByText('Success')).toBeInTheDocument() + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText('Error')).not.toBeInTheDocument() + }) +}) diff --git a/packages/svelte-query/src/__tests__/utils.ts b/packages/svelte-query/src/__tests__/utils.ts new file mode 100644 index 0000000000..bbdcf88a96 --- /dev/null +++ b/packages/svelte-query/src/__tests__/utils.ts @@ -0,0 +1,72 @@ +import { vi } from 'vitest' +import { act } from '@testing-library/svelte' +import { + QueryClient, + type QueryClientConfig, + type MutationOptions, +} from '../index' + +export function createQueryClient(config?: QueryClientConfig): QueryClient { + vi.spyOn(console, 'error').mockImplementation(() => undefined) + return new QueryClient({ logger: mockLogger, ...config }) +} + +export function mockVisibilityState(value: DocumentVisibilityState) { + return vi.spyOn(document, 'visibilityState', 'get').mockReturnValue(value) +} + +export function mockNavigatorOnLine(value: boolean) { + return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) +} + +export const mockLogger = { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn(), +} + +let queryKeyCount = 0 +export function queryKey(): Array { + queryKeyCount++ + return [`query_${queryKeyCount}`] +} + +export function sleep(timeout: number): Promise { + return new Promise((resolve, _reject) => { + setTimeout(resolve, timeout) + }) +} + +export async function simplefetcher() { + await sleep(10) + return 'test' +} + +export function setActTimeout(fn: () => void, ms?: number) { + return setTimeout(() => { + act(() => { + fn() + }) + }, ms) +} + +/** + * Assert the parameter is of a specific type. + */ +export function expectType(_: T): void { + return undefined +} + +/** + * Assert the parameter is not typed as `any` + */ +export function expectTypeNotAny(_: 0 extends 1 & T ? never : T): void { + return undefined +} + +export function executeMutation( + queryClient: QueryClient, + options: MutationOptions, +): Promise { + return queryClient.getMutationCache().build(queryClient, options).execute() +} diff --git a/packages/svelte-query/src/context.ts b/packages/svelte-query/src/context.ts new file mode 100644 index 0000000000..d907da4732 --- /dev/null +++ b/packages/svelte-query/src/context.ts @@ -0,0 +1,21 @@ +import { setContext, getContext } from 'svelte' +import type { QueryClient } from '@tanstack/query-core' + +const _contextKey = '$$_queryClient' + +/** Retrieves a Client from Svelte's context */ +export const getQueryClientContext = (): QueryClient => { + const client = getContext(_contextKey) + if (!client) { + throw new Error( + 'No QueryClient was found in Svelte context. Did you forget to wrap your component with QueryClientProvider?', + ) + } + + return client as QueryClient +} + +/** Sets a QueryClient on Svelte's context */ +export const setQueryClientContext = (client: QueryClient): void => { + setContext(_contextKey, client) +} diff --git a/packages/svelte-query/src/createBaseQuery.ts b/packages/svelte-query/src/createBaseQuery.ts new file mode 100644 index 0000000000..9aa81df71f --- /dev/null +++ b/packages/svelte-query/src/createBaseQuery.ts @@ -0,0 +1,74 @@ +import type { QueryClient, QueryKey, QueryObserver } from '@tanstack/query-core' +import { notifyManager } from '@tanstack/query-core' +import type { CreateBaseQueryOptions, CreateBaseQueryResult } from './types' +import { useQueryClient } from './useQueryClient' +import { derived, readable } from 'svelte/store' + +export function createBaseQuery< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey extends QueryKey, +>( + options: CreateBaseQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + Observer: typeof QueryObserver, + queryClient?: QueryClient, +): CreateBaseQueryResult { + const client = useQueryClient(queryClient) + const defaultedOptions = client.defaultQueryOptions(options) + defaultedOptions._optimisticResults = 'optimistic' + + let observer = new Observer< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >(client, defaultedOptions) + + // Include callbacks in batch renders + if (defaultedOptions.onError) { + defaultedOptions.onError = notifyManager.batchCalls( + defaultedOptions.onError, + ) + } + + if (defaultedOptions.onSuccess) { + defaultedOptions.onSuccess = notifyManager.batchCalls( + defaultedOptions.onSuccess, + ) + } + + if (defaultedOptions.onSettled) { + defaultedOptions.onSettled = notifyManager.batchCalls( + defaultedOptions.onSettled, + ) + } + + readable(observer).subscribe(($observer) => { + observer = $observer + // Do not notify on updates because of changes in the options because + // these changes should already be reflected in the optimistic result. + observer.setOptions(defaultedOptions, { listeners: false }) + }) + + const result = readable(observer.getCurrentResult(), (set) => { + return observer.subscribe(notifyManager.batchCalls(set)) + }) + + const { subscribe } = derived(result, ($result) => { + $result = observer.getOptimisticResult(defaultedOptions) + return !defaultedOptions.notifyOnChangeProps + ? observer.trackResult($result) + : $result + }) + + return { subscribe } +} diff --git a/packages/svelte-query/src/createInfiniteQuery.ts b/packages/svelte-query/src/createInfiniteQuery.ts new file mode 100644 index 0000000000..c398805c7c --- /dev/null +++ b/packages/svelte-query/src/createInfiniteQuery.ts @@ -0,0 +1,29 @@ +import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' +import { InfiniteQueryObserver } from '@tanstack/query-core' +import type { + CreateInfiniteQueryOptions, + CreateInfiniteQueryResult, +} from './types' +import { createBaseQuery } from './createBaseQuery' + +export function createInfiniteQuery< + TQueryFnData, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + queryClient?: QueryClient, +): CreateInfiniteQueryResult { + return createBaseQuery( + options, + InfiniteQueryObserver as typeof QueryObserver, + queryClient, + ) as CreateInfiniteQueryResult +} diff --git a/packages/svelte-query/src/createMutation.ts b/packages/svelte-query/src/createMutation.ts new file mode 100644 index 0000000000..272737eb7b --- /dev/null +++ b/packages/svelte-query/src/createMutation.ts @@ -0,0 +1,49 @@ +import { readable, derived } from 'svelte/store' +import type { QueryClient } from '@tanstack/query-core' +import { MutationObserver, notifyManager } from '@tanstack/query-core' +import type { + CreateMutateFunction, + CreateMutationOptions, + CreateMutationResult, +} from './types' +import { useQueryClient } from './useQueryClient' + +export function createMutation< + TData = unknown, + TError = Error, + TVariables = void, + TContext = unknown, +>( + options: CreateMutationOptions, + queryClient?: QueryClient, +): CreateMutationResult { + const client = useQueryClient(queryClient) + let observer = new MutationObserver( + client, + options, + ) + let mutate: CreateMutateFunction + + readable(observer).subscribe(($observer) => { + observer = $observer + mutate = (variables, mutateOptions) => { + observer.mutate(variables, mutateOptions).catch(noop) + } + observer.setOptions(options) + }) + + const result = readable(observer.getCurrentResult(), (set) => { + return observer.subscribe(notifyManager.batchCalls((val) => set(val))) + }) + + const { subscribe } = derived(result, ($result) => ({ + ...$result, + mutate, + mutateAsync: $result.mutate, + })) + + return { subscribe } +} + +// eslint-disable-next-line @typescript-eslint/no-empty-function +function noop() {} diff --git a/packages/svelte-query/src/createQueries.ts b/packages/svelte-query/src/createQueries.ts new file mode 100644 index 0000000000..28a8b9b4b5 --- /dev/null +++ b/packages/svelte-query/src/createQueries.ts @@ -0,0 +1,190 @@ +import type { + QueryKey, + QueryFunction, + QueryClient, + QueriesPlaceholderDataFunction, +} from '@tanstack/query-core' + +import { notifyManager, QueriesObserver } from '@tanstack/query-core' +import { readable, type Readable } from 'svelte/store' + +import type { CreateQueryOptions, CreateQueryResult } from './types' +import { useQueryClient } from './useQueryClient' + +// This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. +// `placeholderData` function does not have a parameter +type CreateQueryOptionsForCreateQueries< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = Omit< + CreateQueryOptions, + 'placeholderData' +> & { + placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction +} + +// Avoid TS depth-limit error in case of large array literal +type MAXIMUM_DEPTH = 20 + +type GetOptions = + // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData } + T extends { + queryFnData: infer TQueryFnData + error?: infer TError + data: infer TData + } + ? CreateQueryOptionsForCreateQueries + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? CreateQueryOptionsForCreateQueries + : T extends { data: infer TData; error?: infer TError } + ? CreateQueryOptionsForCreateQueries + : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData] + T extends [infer TQueryFnData, infer TError, infer TData] + ? CreateQueryOptionsForCreateQueries + : T extends [infer TQueryFnData, infer TError] + ? CreateQueryOptionsForCreateQueries + : T extends [infer TQueryFnData] + ? CreateQueryOptionsForCreateQueries + : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided + T extends { + queryFn?: QueryFunction + select: (data: any) => infer TData + } + ? CreateQueryOptionsForCreateQueries + : T extends { queryFn?: QueryFunction } + ? CreateQueryOptionsForCreateQueries< + TQueryFnData, + Error, + TQueryFnData, + TQueryKey + > + : // Fallback + CreateQueryOptionsForCreateQueries + +type GetResults = + // Part 1: responsible for mapping explicit type parameter to function result, if object + T extends { queryFnData: any; error?: infer TError; data: infer TData } + ? CreateQueryResult + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? CreateQueryResult + : T extends { data: infer TData; error?: infer TError } + ? CreateQueryResult + : // Part 2: responsible for mapping explicit type parameter to function result, if tuple + T extends [any, infer TError, infer TData] + ? CreateQueryResult + : T extends [infer TQueryFnData, infer TError] + ? CreateQueryResult + : T extends [infer TQueryFnData] + ? CreateQueryResult + : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided + T extends { + queryFn?: QueryFunction + select: (data: any) => infer TData + } + ? CreateQueryResult + : T extends { queryFn?: QueryFunction } + ? CreateQueryResult + : // Fallback + CreateQueryResult + +/** + * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + */ +export type QueriesOptions< + T extends any[], + Result extends any[] = [], + Depth extends ReadonlyArray = [], +> = Depth['length'] extends MAXIMUM_DEPTH + ? CreateQueryOptionsForCreateQueries[] + : T extends [] + ? [] + : T extends [infer Head] + ? [...Result, GetOptions] + : T extends [infer Head, ...infer Tail] + ? QueriesOptions<[...Tail], [...Result, GetOptions], [...Depth, 1]> + : unknown[] extends T + ? T + : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type! + // use this to infer the param types in the case of Array.map() argument + T extends CreateQueryOptionsForCreateQueries< + infer TQueryFnData, + infer TError, + infer TData, + infer TQueryKey + >[] + ? CreateQueryOptionsForCreateQueries[] + : // Fallback + CreateQueryOptionsForCreateQueries[] + +/** + * QueriesResults reducer recursively maps type param to results + */ +export type QueriesResults< + T extends any[], + Result extends any[] = [], + Depth extends ReadonlyArray = [], +> = Depth['length'] extends MAXIMUM_DEPTH + ? CreateQueryResult[] + : T extends [] + ? [] + : T extends [infer Head] + ? [...Result, GetResults] + : T extends [infer Head, ...infer Tail] + ? QueriesResults<[...Tail], [...Result, GetResults], [...Depth, 1]> + : T extends CreateQueryOptionsForCreateQueries< + infer TQueryFnData, + infer TError, + infer TData, + any + >[] + ? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results + CreateQueryResult< + unknown extends TData ? TQueryFnData : TData, + unknown extends TError ? Error : TError + >[] + : // Fallback + CreateQueryResult[] + +export type CreateQueriesResult = Readable> + +export function createQueries({ + queries, + queryClient, +}: { + queries: readonly [...QueriesOptions] + queryClient?: QueryClient +}): CreateQueriesResult { + const client = useQueryClient(queryClient) + // const isRestoring = useIsRestoring() + + function getDefaultQuery(newQueries: readonly [...QueriesOptions]) { + return newQueries.map((options) => { + const defaultedOptions = client.defaultQueryOptions(options) + // Make sure the results are already in fetching state before subscribing or updating options + defaultedOptions._optimisticResults = 'optimistic' + + return defaultedOptions + }) + } + + const defaultedQueries = getDefaultQuery(queries) + let observer = new QueriesObserver(client, defaultedQueries) + + readable(observer).subscribe(($observer) => { + observer = $observer + // Do not notify on updates because of changes in the options because + // these changes should already be reflected in the optimistic result. + observer.setQueries(defaultedQueries, { listeners: false }) + }) + + const { subscribe } = readable( + observer.getOptimisticResult(defaultedQueries) as any, + (set) => { + return observer.subscribe(notifyManager.batchCalls(set)) + }, + ) + + return { subscribe } +} diff --git a/packages/svelte-query/src/createQuery.ts b/packages/svelte-query/src/createQuery.ts new file mode 100644 index 0000000000..3dd74bcf6e --- /dev/null +++ b/packages/svelte-query/src/createQuery.ts @@ -0,0 +1,58 @@ +import { QueryObserver } from '@tanstack/query-core' +import type { QueryKey, QueryClient } from '@tanstack/query-core' +import { createBaseQuery } from './createBaseQuery' +import type { + DefinedCreateQueryResult, + CreateQueryOptions, + CreateQueryResult, +} from './types' + +type UndefinedInitialDataOptions< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = CreateQueryOptions & { + initialData?: undefined +} + +type DefinedInitialDataOptions< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = CreateQueryOptions & { + initialData: TQueryFnData | (() => TQueryFnData) +} + +export function createQuery< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: UndefinedInitialDataOptions, + queryClient?: QueryClient, +): CreateQueryResult + +export function createQuery< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: DefinedInitialDataOptions, + queryClient?: QueryClient, +): DefinedCreateQueryResult + +export function createQuery< + TQueryFnData, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: CreateQueryOptions, + queryClient?: QueryClient, +) { + return createBaseQuery(options, QueryObserver, queryClient) +} diff --git a/packages/svelte-query/src/index.ts b/packages/svelte-query/src/index.ts new file mode 100644 index 0000000000..c6d4e9e1a7 --- /dev/null +++ b/packages/svelte-query/src/index.ts @@ -0,0 +1,17 @@ +/* istanbul ignore file */ + +// Re-export core +export * from '@tanstack/query-core' + +// Svelte Query +export * from './types' +export { createQuery } from './createQuery' +export { createQueries } from './createQueries' +export { createInfiniteQuery } from './createInfiniteQuery' +export { createMutation } from './createMutation' +export { useQueryClient } from './useQueryClient' +export { useIsFetching } from './useIsFetching' +export { useIsMutating } from './useIsMutating' +export { useHydrate } from './useHydrate' +export { default as HydrationBoundary } from './HydrationBoundary.svelte' +export { default as QueryClientProvider } from './QueryClientProvider.svelte' diff --git a/packages/svelte-query/src/types.ts b/packages/svelte-query/src/types.ts new file mode 100644 index 0000000000..844e3bc3e9 --- /dev/null +++ b/packages/svelte-query/src/types.ts @@ -0,0 +1,127 @@ +import type { + InfiniteQueryObserverOptions, + InfiniteQueryObserverResult, + MutationObserverResult, + QueryObserverOptions, + QueryObserverResult, + QueryKey, + MutationObserverOptions, + MutateFunction, + DefinedQueryObserverResult, +} from '@tanstack/query-core' +import type { QueryClient } from '@tanstack/query-core' +import type { Readable } from 'svelte/store' + +export interface ContextOptions { + /** + * Use this to pass your Svelte Query context. Otherwise, `defaultContext` will be used. + */ + context?: QueryClient | undefined +} + +export interface CreateBaseQueryOptions< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends ContextOptions, + QueryObserverOptions {} + +export interface CreateBaseQueryResult + extends Readable> {} + +export interface CreateQueryOptions< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends CreateBaseQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + > {} + +export interface CreateQueryResult + extends CreateBaseQueryResult {} + +export interface CreateInfiniteQueryOptions< + TQueryFnData = unknown, + TError = Error, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > {} + +export type CreateInfiniteQueryResult< + TData = unknown, + TError = Error, +> = Readable> + +export type DefinedCreateBaseQueryResult< + TData = unknown, + TError = Error, +> = Readable> + +export type DefinedCreateQueryResult< + TData = unknown, + TError = Error, +> = DefinedCreateBaseQueryResult + +export interface CreateMutationOptions< + TData = unknown, + TError = Error, + TVariables = void, + TContext = unknown, +> extends ContextOptions, + Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} + +export type CreateMutateFunction< + TData = unknown, + TError = Error, + TVariables = void, + TContext = unknown, +> = ( + ...args: Parameters> +) => void + +export type CreateMutateAsyncFunction< + TData = unknown, + TError = Error, + TVariables = void, + TContext = unknown, +> = MutateFunction + +export type CreateBaseMutationResult< + TData = unknown, + TError = Error, + TVariables = unknown, + TContext = unknown, +> = Override< + MutationObserverResult, + { mutate: CreateMutateFunction } +> & { + mutateAsync: CreateMutateAsyncFunction +} + +export interface CreateMutationResult< + TData = unknown, + TError = Error, + TVariables = unknown, + TContext = unknown, +> extends Readable< + CreateBaseMutationResult + > {} + +type Override = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } diff --git a/packages/svelte-query/src/useHydrate.ts b/packages/svelte-query/src/useHydrate.ts new file mode 100644 index 0000000000..709b4a468c --- /dev/null +++ b/packages/svelte-query/src/useHydrate.ts @@ -0,0 +1,18 @@ +import { + type HydrateOptions, + type QueryClient, + hydrate, +} from '@tanstack/query-core' +import { useQueryClient } from './useQueryClient' + +export function useHydrate( + state?: unknown, + options?: HydrateOptions, + queryClient?: QueryClient, +) { + const client = useQueryClient(queryClient) + + if (state) { + hydrate(client, state, options) + } +} diff --git a/packages/svelte-query/src/useIsFetching.ts b/packages/svelte-query/src/useIsFetching.ts new file mode 100644 index 0000000000..86d5e2b23f --- /dev/null +++ b/packages/svelte-query/src/useIsFetching.ts @@ -0,0 +1,32 @@ +import { + type QueryFilters, + type QueryClient, + notifyManager, +} from '@tanstack/query-core' +import { type Readable, readable } from 'svelte/store' +import { useQueryClient } from './useQueryClient' + +export function useIsFetching( + filters?: QueryFilters, + queryClient?: QueryClient, +): Readable { + const client = useQueryClient(queryClient) + const cache = client.getQueryCache() + // isFetching is the prev value initialized on mount * + let isFetching = client.isFetching(filters) + + const { subscribe } = readable(isFetching, (set) => { + return cache.subscribe( + notifyManager.batchCalls(() => { + const newIsFetching = client.isFetching(filters) + if (isFetching !== newIsFetching) { + // * and update with each change + isFetching = newIsFetching + set(isFetching) + } + }), + ) + }) + + return { subscribe } +} diff --git a/packages/svelte-query/src/useIsMutating.ts b/packages/svelte-query/src/useIsMutating.ts new file mode 100644 index 0000000000..c4293fa744 --- /dev/null +++ b/packages/svelte-query/src/useIsMutating.ts @@ -0,0 +1,32 @@ +import { + type MutationFilters, + type QueryClient, + notifyManager, +} from '@tanstack/query-core' +import { type Readable, readable } from 'svelte/store' +import { useQueryClient } from './useQueryClient' + +export function useIsMutating( + filters?: MutationFilters, + queryClient?: QueryClient, +): Readable { + const client = useQueryClient(queryClient) + const cache = client.getMutationCache() + // isMutating is the prev value initialized on mount * + let isMutating = client.isMutating(filters) + + const { subscribe } = readable(isMutating, (set) => { + return cache.subscribe( + notifyManager.batchCalls(() => { + const newIisMutating = client.isMutating(filters) + if (isMutating !== newIisMutating) { + // * and update with each change + isMutating = newIisMutating + set(isMutating) + } + }), + ) + }) + + return { subscribe } +} diff --git a/packages/svelte-query/src/useQueryClient.ts b/packages/svelte-query/src/useQueryClient.ts new file mode 100644 index 0000000000..4e8b2bcf94 --- /dev/null +++ b/packages/svelte-query/src/useQueryClient.ts @@ -0,0 +1,12 @@ +import type { QueryClient } from '@tanstack/query-core' +import { getQueryClientContext } from './context' + +export function useQueryClient(queryClient?: QueryClient): QueryClient { + const client = getQueryClientContext() + + if (queryClient) { + return queryClient + } + + return client +} diff --git a/packages/svelte-query/svelte.config.js b/packages/svelte-query/svelte.config.js new file mode 100644 index 0000000000..b1eea11b9d --- /dev/null +++ b/packages/svelte-query/svelte.config.js @@ -0,0 +1,14 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +const config = { + preprocess: vitePreprocess(), + package: { + source: "./src", + dir: "./build/lib", + files: (filepath) => { + return !filepath.includes("__tests__"); + }, + }, +}; + +export default config; diff --git a/packages/svelte-query/tsconfig.json b/packages/svelte-query/tsconfig.json new file mode 100644 index 0000000000..d7bfd5a72f --- /dev/null +++ b/packages/svelte-query/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "checkJs": true, + "composite": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "importsNotUsedAsValues": "error", + "isolatedModules": true, + "preserveValueImports": true, + "lib": ["esnext", "DOM", "DOM.Iterable"], + "moduleResolution": "node", + "module": "esnext", + "noEmit": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "rootDir": "./src", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "target": "esnext", + "tsBuildInfoFile": "./build/.tsbuildinfo" + }, + "include": ["src"], + "references": [ + { "path": "../query-core" } + ] +} diff --git a/packages/svelte-query/vite.config.ts b/packages/svelte-query/vite.config.ts new file mode 100644 index 0000000000..13cf4e6bf5 --- /dev/null +++ b/packages/svelte-query/vite.config.ts @@ -0,0 +1,22 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import path from 'path'; +import type { UserConfig } from 'vite'; + +const config: UserConfig = { + plugins: [svelte()], + resolve: { + alias: { + "@tanstack/query-core": path.resolve(__dirname, '..', 'query-core', 'src'), + } + }, + test: { + coverage: { + provider: 'istanbul' + }, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,ts}'], + setupFiles: ['vitest.setup.ts'] + } +}; + +export default config; diff --git a/packages/svelte-query/vitest.setup.ts b/packages/svelte-query/vitest.setup.ts new file mode 100644 index 0000000000..b210af530c --- /dev/null +++ b/packages/svelte-query/vitest.setup.ts @@ -0,0 +1,4 @@ +import matchers from '@testing-library/jest-dom/matchers'; +import { expect } from 'vitest'; + +expect.extend(matchers); diff --git a/packages/vue-query/README.md b/packages/vue-query/README.md index a882b1c09a..2125d07623 100644 --- a/packages/vue-query/README.md +++ b/packages/vue-query/README.md @@ -79,5 +79,5 @@ Visit https://tanstack.com/query/v4/docs/adapters/vue-query const id = ref(1); const enabled = ref(false); - const query = useQuery({ queyKey: ["todos", id], queryFn: () => getTodos(id), enabled }); + const query = useQuery({ queryKey: ["todos", id], queryFn: () => getTodos(id), enabled }); ``` diff --git a/packages/vue-query/package.json b/packages/vue-query/package.json index fa0f7addd9..40950f13c3 100644 --- a/packages/vue-query/package.json +++ b/packages/vue-query/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-query", - "version": "4.20.9", + "version": "4.22.4", "description": "Hooks for managing, caching and syncing asynchronous and remote data in Vue", "author": "Damian Osipiuk", "license": "MIT", @@ -27,10 +27,13 @@ }, "sideEffects": false, "scripts": { - "clean": "rm -rf ./build", - "test:eslint": "../../node_modules/.bin/eslint --ext .ts ./src", - "test:jest": "../../node_modules/.bin/jest --config ./jest.config.ts", - "test:dev": "pnpm run test:jest --watch" + "clean": "rimraf ./build", + "test:eslint": "eslint --ext .ts ./src", + "test:types": "tsc", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch", + "test:lib:publish": "pnpm run test:lib --collectCoverage false", + "build:types": "tsc --build" }, "files": [ "build/lib/*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e03748a32e..8cafcc2a34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,7 +17,6 @@ importers: '@testing-library/jest-dom': ^5.16.4 '@testing-library/react': ^13.0.0 '@testing-library/react-hooks': ^7.0.2 - '@tsconfig/svelte': ^3.0.0 '@types/jest': ^26.0.4 '@types/luxon': ^2.3.1 '@types/node': ^17.0.25 @@ -54,17 +53,17 @@ importers: jsonfile: ^6.1.0 luxon: ^2.3.2 prettier: ^2.6.2 + prettier-plugin-svelte: ^2.9.0 react: ^18.2.0 react-dom: ^18.2.0 + rimraf: ^3.0.2 rollup: ^2.70.2 rollup-plugin-size: ^0.2.2 - rollup-plugin-svelte: ^7.1.0 rollup-plugin-terser: ^7.0.2 rollup-plugin-visualizer: ^5.6.0 solid-js: ^1.5.7 solid-testing-library: ^0.3.0 stream-to-array: ^2.3.0 - svelte: ^3.48.0 ts-jest: ^27.1.1 ts-node: ^10.7.0 typescript: 4.7.4 @@ -83,7 +82,6 @@ importers: '@testing-library/jest-dom': 5.16.4 '@testing-library/react': 13.3.0_biqbaboplfbrettd7655fr4n2y '@testing-library/react-hooks': 7.0.2_biqbaboplfbrettd7655fr4n2y - '@tsconfig/svelte': 3.0.0 '@types/jest': 26.0.24 '@types/luxon': 2.3.2 '@types/node': 17.0.45 @@ -120,17 +118,17 @@ importers: jsonfile: 6.1.0 luxon: 2.4.0 prettier: 2.7.1 + prettier-plugin-svelte: 2.9.0_prettier@2.7.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 + rimraf: 3.0.2 rollup: 2.78.1 rollup-plugin-size: 0.2.2 - rollup-plugin-svelte: 7.1.0_7rgskvp3hfjz55pue4td2a2xga rollup-plugin-terser: 7.0.2_rollup@2.78.1 rollup-plugin-visualizer: 5.6.0_rollup@2.78.1 solid-js: 1.5.7 solid-testing-library: 0.3.0_solid-js@1.5.7 stream-to-array: 2.3.0 - svelte: 3.49.0 ts-jest: 27.1.5_f5rbw76fdbj6kcejdlin5zoydm ts-node: 10.8.2_x2utdhayajzrh747hktprshhby typescript: 4.7.4 @@ -636,6 +634,180 @@ importers: vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 + examples/svelte/auto-refetching: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/basic: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/load-more-infinite-scroll: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/optimistic-updates-typescript: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/playground: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/simple: + specifiers: + '@sveltejs/vite-plugin-svelte': ^2.0.2 + '@tanstack/svelte-query': ^4.20.0 + '@tsconfig/svelte': ^3.0.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.0+vite@4.0.4 + '@tsconfig/svelte': 3.0.0 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/ssr: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + + examples/svelte/star-wars: + specifiers: + '@sveltejs/adapter-auto': ^1.0.0 + '@sveltejs/kit': ^1.0.0 + '@tanstack/svelte-query': ^4.20.0 + autoprefixer: ^10.4.13 + postcss: ^8.4.20 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tailwindcss: ^3.2.4 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + dependencies: + '@tanstack/svelte-query': link:../../../packages/svelte-query + devDependencies: + '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + autoprefixer: 10.4.13_postcss@8.4.21 + postcss: 8.4.21 + svelte: 3.55.0 + svelte-check: 2.10.3_77wbasr76lhjripnylrva3hecy + tailwindcss: 3.2.4_postcss@8.4.21 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + examples/vue/basic: specifiers: '@tanstack/vue-query': ^4.13.3 @@ -796,6 +968,37 @@ importers: devDependencies: solid-jest: 0.2.0 + packages/svelte-query: + specifiers: + '@sveltejs/package': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^2.0.2 + '@tanstack/query-core': workspace:* + '@testing-library/svelte': ^3.2.2 + '@vitest/coverage-istanbul': ^0.27.1 + eslint-plugin-svelte: ^2.14.1 + jsdom: ^20.0.3 + svelte: ^3.54.0 + svelte-check: ^2.9.2 + tslib: ^2.4.1 + typescript: ^4.7.4 + vite: ^4.0.0 + vitest: ^0.27.1 + dependencies: + '@tanstack/query-core': link:../query-core + devDependencies: + '@sveltejs/package': 1.0.2_glsdxddlaertg66rhhvanbinpy + '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.0+vite@4.0.4 + '@testing-library/svelte': 3.2.2_svelte@3.55.0 + '@vitest/coverage-istanbul': 0.27.1_jsdom@20.0.3 + eslint-plugin-svelte: 2.14.1_svelte@3.55.0 + jsdom: 20.0.3 + svelte: 3.55.0 + svelte-check: 2.10.3_svelte@3.55.0 + tslib: 2.4.1 + typescript: 4.8.4 + vite: 4.0.4 + vitest: 0.27.1_jsdom@20.0.3 + packages/vue-query: specifiers: '@tanstack/match-sorter-utils': ^8.1.1 @@ -3964,6 +4167,96 @@ packages: dev: true optional: true + /@esbuild/android-arm/0.16.15: + resolution: {integrity: sha512-JsJtmadyWcR+DEtHLixM7bAQsfi1s0Xotv9kVOoXbCLyhKPOHvMEyh3kJBuTbCPSE4c2jQkQVmarwc9Mg9k3bA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64/0.16.15: + resolution: {integrity: sha512-OdbkUv7468dSsgoFtHIwTaYAuI5lDEv/v+dlfGBUbVa2xSDIIuSOHXawynw5N9+5lygo/JdXa5/sgGjiEU18gQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64/0.16.15: + resolution: {integrity: sha512-dPUOBiNNWAm+/bxoA75o7R7qqqfcEzXaYlb5uJk2xGHmUMNKSAnDCtRYLgx9/wfE4sXyn8H948OrDyUAHhPOuA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64/0.16.15: + resolution: {integrity: sha512-AksarYV85Hxgwh5/zb6qGl4sYWxIXPQGBAZ+jUro1ZpINy3EWumK+/4DPOKUBPnsrOIvnNXy7Rq4mTeCsMQDNA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64/0.16.15: + resolution: {integrity: sha512-qqrKJxoohceZGGP+sZ5yXkzW9ZiyFZJ1gWSEfuYdOWzBSL18Uy3w7s/IvnDYHo++/cxwqM0ch3HQVReSZy7/4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64/0.16.15: + resolution: {integrity: sha512-LBWaep6RvJm5KnsKkocdVEzuwnGMjz54fcRVZ9d3R7FSEWOtPBxMhuxeA1n98JVbCLMkTPFmKN6xSnfhnM9WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64/0.16.15: + resolution: {integrity: sha512-LE8mKC6JPR04kPLRP9A6k7ZmG0k2aWF4ru79Sde6UeWCo7yDby5f48uJNFQ2pZqzUUkLrHL8xNdIHerJeZjHXg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm/0.16.15: + resolution: {integrity: sha512-+1sGlqtMJTOnJUXwLUGnDhPaGRKqxT0UONtYacS+EjdDOrSgpQ/1gUXlnze45Z/BogwYaswQM19Gu1YD1T19/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64/0.16.15: + resolution: {integrity: sha512-mRYpuQGbzY+XLczy3Sk7fMJ3DRKLGDIuvLKkkUkyecDGQMmil6K/xVKP9IpKO7JtNH477qAiMjjX7jfKae8t4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32/0.16.15: + resolution: {integrity: sha512-puXVFvY4m8EB6/fzu3LdgjiNnEZ3gZMSR7NmKoQe51l3hyQalvTjab3Dt7aX4qGf+8Pj7dsCOBNzNzkSlr/4Aw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.15.9: resolution: {integrity: sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==} engines: {node: '>=12'} @@ -3973,6 +4266,114 @@ packages: dev: true optional: true + /@esbuild/linux-loong64/0.16.15: + resolution: {integrity: sha512-ATMGb3eg8T6ZTGZFldlGeFEcevBiVq6SBHvRAO04HMfUjZWneZ/U+JJb3YzlNZxuscJ4Tmzq+JrYxlk7ro4dRg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el/0.16.15: + resolution: {integrity: sha512-3SEA4L82OnoSATW+Ve8rPgLaKjC8WMt8fnx7De9kvi/NcVbkj8W+J7qnu/tK2P9pUPQP7Au/0sjPEqZtFeyKQQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64/0.16.15: + resolution: {integrity: sha512-8PgbeX+N6vmqeySzyxO0NyDOltCEW13OS5jUHTvCHmCgf4kNXZtAWJ+zEfJxjRGYhVezQ1FdIm7WfN1R27uOyg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64/0.16.15: + resolution: {integrity: sha512-U+coqH+89vbPVoU30no1Fllrn6gvEeO5tfEArBhjYZ+dQ3Gv7ciQXYf5nrT1QdlIFwEjH4Is1U1iiaGWW+tGpQ==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x/0.16.15: + resolution: {integrity: sha512-M0nKLFMdyFGBoitxG42kq6Xap0CPeDC6gfF9lg7ZejzGF6kqYUGT+pQGl2QCQoxJBeat/LzTma1hG8C3dq2ocg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64/0.16.15: + resolution: {integrity: sha512-t7/fOXBUKfigvhJLGKZ9TPHHgqNgpIpYaAbcXQk1X+fPeUG7x0tpAbXJ2wST9F/gJ02+CLETPMnhG7Tra2wqsQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64/0.16.15: + resolution: {integrity: sha512-0k0Nxi6DOJmTnLtKD/0rlyqOPpcqONXY53vpkoAsue8CfyhNPWtwzba1ICFNCfCY1dqL3Ho/xEzujJhmdXq1rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64/0.16.15: + resolution: {integrity: sha512-3SkckazfIbdSjsGpuIYT3d6n2Hx0tck3MS1yVsbahhWiLvdy4QozTpvlbjqO3GmvtvhxY4qdyhFOO2wiZKeTAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64/0.16.15: + resolution: {integrity: sha512-8PNvBC+O8X5EnyIGqE8St2bOjjrXMR17NOLenIrzolvwWnJXvwPo0tE/ahOeiAJmTOS/eAcN8b4LAZcn17Uj7w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64/0.16.15: + resolution: {integrity: sha512-YPaSgm/mm7kNcATB53OxVGVfn6rDNbImTn330ZlF3hKej1e9ktCaljGjn2vH08z2dlHEf3kdt57tNjE6zs8SzA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32/0.16.15: + resolution: {integrity: sha512-0movUXbSNrTeNf5ZXT0avklEvlJD0hNGZsrrXHfsp9z4tK5xC+apCqmUEZeE9mqrb84Z8XbgGr/MS9LqafTP2A==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64/0.16.15: + resolution: {integrity: sha512-27h5GCcbfomVAqAnMJWvR1LqEY0dFqIq4vTe5nY3becnZNu0SX8F0+gTk3JPvgWQHzaGc6VkPzlOiMkdSUunUA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint/eslintrc/0.4.3: resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -4424,10 +4825,10 @@ packages: glob: 7.2.3 graceful-fs: 4.2.10 istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.0 + istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.4 + istanbul-reports: 3.1.5 jest-haste-map: 27.5.1 jest-resolve: 27.5.1 jest-util: 27.5.1 @@ -5201,6 +5602,10 @@ packages: resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} dev: false + /@polka/url/1.0.0-next.21: + resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} + dev: true + /@react-native-community/cli-debugger-ui/5.0.1: resolution: {integrity: sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA==} dependencies: @@ -5536,10 +5941,82 @@ packages: '@sinonjs/commons': 1.8.3 dev: true + /@sveltejs/adapter-auto/1.0.0_@sveltejs+kit@1.0.7: + resolution: {integrity: sha512-yKyPvlLVua1bJ/42FrR3X041mFGdB4GzTZOAEoHUcNBRE5Mhx94+eqHpC3hNvAOiLEDcKfVO0ObyKSu7qldU+w==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + dependencies: + '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 + import-meta-resolve: 2.2.1 + dev: true + + /@sveltejs/kit/1.0.7_svelte@3.55.0+vite@4.0.4: + resolution: {integrity: sha512-u8JS4aXFWlrnu/tjl+EhJ/FvBEjLYDyMaLe7EAU4sW+PfDqnqyHBAPg/IQi5JuBg6l+Z816F4WrTe+zplUTQDg==} + engines: {node: ^16.14 || >=18} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.54.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.0+vite@4.0.4 + '@types/cookie': 0.5.1 + cookie: 0.5.0 + devalue: 4.2.0 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.27.0 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.5.1 + sirv: 2.0.2 + svelte: 3.55.0 + tiny-glob: 0.2.9 + undici: 5.14.0 + vite: 4.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/package/1.0.2_glsdxddlaertg66rhhvanbinpy: + resolution: {integrity: sha512-VY9U+05d9uNFDj7ScKRlHORYlfPSHwJewBjV+V2RsnViexpLFPUrboC9SiPYDCpLnbeqwXerxhO6twGHUBGeIA==} + engines: {node: ^16.14 || >=18} + hasBin: true + peerDependencies: + svelte: ^3.44.0 + dependencies: + chokidar: 3.5.3 + kleur: 4.1.5 + sade: 1.8.1 + svelte: 3.55.0 + svelte2tsx: 0.6.0_glsdxddlaertg66rhhvanbinpy + transitivePeerDependencies: + - typescript + dev: true + + /@sveltejs/vite-plugin-svelte/2.0.2_svelte@3.55.0+vite@4.0.4: + resolution: {integrity: sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 + vite: ^4.0.0 + dependencies: + debug: 4.3.4 + deepmerge: 4.2.2 + kleur: 4.1.5 + magic-string: 0.27.0 + svelte: 3.55.0 + svelte-hmr: 0.15.1_svelte@3.55.0 + vite: 4.0.4 + vitefu: 0.2.4_vite@4.0.4 + transitivePeerDependencies: + - supports-color + dev: true + /@swc/helpers/0.4.2: resolution: {integrity: sha512-556Az0VX7WR6UdoTn4htt/l3zPQ7bsQWK+HqdG4swV7beUCxo/BqmvbOpUkTIm/9ih86LIf1qsUnywNL3obGHw==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /@tanstack/match-sorter-utils/8.1.1: @@ -5597,6 +6074,20 @@ packages: pretty-format: 27.5.1 dev: true + /@testing-library/dom/8.18.1: + resolution: {integrity: sha512-oEvsm2B/WtcHKE+IcEeeCqNU/ltFGaVyGbpcm4g/2ytuT49jrlH9x5qRKL/H3A6yfM4YAbSbC0ceT5+9CEXnLg==} + engines: {node: '>=12'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/runtime': 7.19.0 + '@types/aria-query': 4.2.2 + aria-query: 5.0.2 + chalk: 4.1.2 + dom-accessibility-api: 0.5.14 + lz-string: 1.4.4 + pretty-format: 27.5.1 + dev: true + /@testing-library/jest-dom/5.16.4: resolution: {integrity: sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==} engines: {node: '>=8', npm: '>=6', yarn: '>=1'} @@ -5648,11 +6139,26 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: true + /@testing-library/svelte/3.2.2_svelte@3.55.0: + resolution: {integrity: sha512-IKwZgqbekC3LpoRhSwhd0JswRGxKdAGkf39UiDXTywK61YyLXbCYoR831e/UUC6EeNW4hiHPY+2WuovxOgI5sw==} + engines: {node: '>= 10'} + peerDependencies: + svelte: 3.x + dependencies: + '@testing-library/dom': 8.18.1 + svelte: 3.55.0 + dev: true + /@tootallnate/once/1.1.2: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} dev: true + /@tootallnate/once/2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -5706,10 +6212,24 @@ packages: '@babel/types': 7.19.0 dev: true + /@types/chai-subset/1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.4 + dev: true + + /@types/chai/4.3.4: + resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + dev: true + /@types/cookie/0.4.1: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} dev: false + /@types/cookie/0.5.1: + resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} + dev: true + /@types/estree/0.0.39: resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} dev: true @@ -5788,7 +6308,11 @@ packages: /@types/prop-types/15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - /@types/react-dom/17.0.17: + /@types/pug/2.0.6: + resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} + dev: true + + /@types/react-dom/17.0.17: resolution: {integrity: sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==} dependencies: '@types/react': 17.0.50 @@ -5839,6 +6363,12 @@ packages: '@types/node': 17.0.45 dev: true + /@types/sass/1.43.1: + resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} + dependencies: + '@types/node': 17.0.45 + dev: true + /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} @@ -5895,7 +6425,7 @@ packages: functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0_typescript@4.4.4 typescript: 4.4.4 transitivePeerDependencies: @@ -6167,7 +6697,7 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0 transitivePeerDependencies: - supports-color @@ -6187,7 +6717,7 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0_typescript@4.7.4 typescript: 4.7.4 transitivePeerDependencies: @@ -6319,6 +6849,30 @@ packages: vue: 3.2.41 dev: true + /@vitest/coverage-istanbul/0.27.1_jsdom@20.0.3: + resolution: {integrity: sha512-VVLwkyRloXb5laEWdCDb5Ns4+W7vtb1PBJR0nLXZRCuzDKH3VeWYmb4xeYn6I9fz9Yv9Vmcke2X/gd3/lKW5Vw==} + dependencies: + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + test-exclude: 6.0.0 + vitest: 0.27.1_jsdom@20.0.3 + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@vitest/browser' + - '@vitest/ui' + - happy-dom + - jsdom + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /@vue/compiler-core/3.2.37: resolution: {integrity: sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==} dependencies: @@ -6381,7 +6935,7 @@ packages: resolution: {integrity: sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==} dependencies: '@babel/parser': 7.19.1 - postcss: 8.4.18 + postcss: 8.4.21 source-map: 0.6.1 dev: true @@ -6396,7 +6950,7 @@ packages: '@vue/shared': 3.2.37 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.18 + postcss: 8.4.21 source-map: 0.6.1 dev: true @@ -6411,7 +6965,7 @@ packages: '@vue/shared': 3.2.39 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.18 + postcss: 8.4.21 source-map: 0.6.1 /@vue/compiler-sfc/3.2.40: @@ -6425,7 +6979,7 @@ packages: '@vue/shared': 3.2.40 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.18 + postcss: 8.4.21 source-map: 0.6.1 /@vue/compiler-sfc/3.2.41: @@ -6439,7 +6993,7 @@ packages: '@vue/shared': 3.2.41 estree-walker: 2.0.2 magic-string: 0.25.9 - postcss: 8.4.18 + postcss: 8.4.21 source-map: 0.6.1 /@vue/compiler-ssr/3.2.37: @@ -6683,6 +7237,13 @@ packages: acorn-walk: 7.2.0 dev: true + /acorn-globals/7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + dependencies: + acorn: 8.8.1 + acorn-walk: 8.2.0 + dev: true + /acorn-jsx/5.3.2_acorn@7.4.1: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -6691,12 +7252,20 @@ packages: acorn: 7.4.1 dev: true - /acorn-jsx/5.3.2_acorn@8.8.0: + /acorn-jsx/5.3.2_acorn@8.8.1: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.8.0 + acorn: 8.8.1 + dev: true + + /acorn-node/1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 dev: true /acorn-walk/7.2.0: @@ -6727,6 +7296,12 @@ packages: hasBin: true dev: true + /acorn/8.8.1: + resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /agent-base/6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -6834,6 +7409,10 @@ packages: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -6948,6 +7527,10 @@ packages: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} dev: false + /assertion-error/1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /assign-symbols/1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} @@ -6960,7 +7543,7 @@ packages: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 /astral-regex/1.0.0: resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} @@ -6994,6 +7577,22 @@ packages: engines: {node: '>= 4.5.0'} hasBin: true + /autoprefixer/10.4.13_postcss@8.4.21: + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.4 + caniuse-lite: 1.0.30001442 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + /axe-core/4.4.2: resolution: {integrity: sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==} engines: {node: '>=12'} @@ -7113,7 +7712,7 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.0 + istanbul-lib-instrument: 5.2.1 test-exclude: 6.0.0 transitivePeerDependencies: - supports-color @@ -7459,7 +8058,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001410 + caniuse-lite: 1.0.30001442 electron-to-chromium: 1.4.258 node-releases: 2.0.6 update-browserslist-db: 1.0.9_browserslist@4.21.4 @@ -7487,6 +8086,10 @@ packages: buffer-fill: 1.0.0 dev: false + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + /buffer-equal/0.0.1: resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==} engines: {node: '>=0.4.0'} @@ -7539,6 +8142,13 @@ packages: - debug dev: true + /busboy/1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: true + /bytes/3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -7598,6 +8208,11 @@ packages: engines: {node: '>=6'} dev: true + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + /camelcase-keys/6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -7617,6 +8232,10 @@ packages: /caniuse-lite/1.0.30001410: resolution: {integrity: sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==} + dev: false + + /caniuse-lite/1.0.30001442: + resolution: {integrity: sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==} /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} @@ -7625,6 +8244,19 @@ packages: rsvp: 4.8.5 dev: false + /chai/4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -7664,6 +8296,10 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: false + /check-error/1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -7894,7 +8530,7 @@ packages: dev: false /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} /concurrently/7.2.2: resolution: {integrity: sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==} @@ -7959,6 +8595,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookie/0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + /copy-anything/3.0.2: resolution: {integrity: sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA==} engines: {node: '>=12.13'} @@ -8076,6 +8717,12 @@ packages: source-map-resolve: 0.6.0 dev: true + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + /cssom/0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} dev: true @@ -8084,6 +8731,10 @@ packages: resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} dev: true + /cssom/0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + dev: true + /cssstyle/2.3.0: resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} engines: {node: '>=8'} @@ -8123,6 +8774,15 @@ packages: whatwg-url: 8.7.0 dev: true + /data-urls/3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + /date-fns/2.28.0: resolution: {integrity: sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==} engines: {node: '>=0.11'} @@ -8187,18 +8847,29 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - /decimal.js/10.3.1: - resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} + /decimal.js/10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true /decode-uri-component/0.2.0: resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==} engines: {node: '>=0.10'} + /dedent-js/1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + dev: true + /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true + /deep-eql/4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -8250,6 +8921,10 @@ packages: is-descriptor: 1.0.2 isobject: 3.0.1 + /defined/1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + dev: true + /delayed-stream/1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -8268,6 +8943,11 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false + /detect-indent/6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + /detect-newline/3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -8277,6 +8957,24 @@ packages: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} dev: false + /detective/5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.6 + dev: true + + /devalue/4.2.0: + resolution: {integrity: sha512-mbjoAaCL2qogBKgeFxFPOXAUsZchircF+B/79LD4sHH0+NHfYm8gZpQrskKDn5gENGt35+5OI1GUF7hLVnkPDw==} + dev: true + + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + /diff-sequences/26.6.2: resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==} engines: {node: '>= 10.14.2'} @@ -8299,6 +8997,10 @@ packages: path-type: 4.0.0 dev: true + /dlv/1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + /doctrine/2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -8335,6 +9037,13 @@ packages: webidl-conversions: 5.0.0 dev: true + /domexception/4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + dependencies: + webidl-conversions: 7.0.0 + dev: true + /dot-prop/5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -8415,6 +9124,11 @@ packages: ansi-colors: 4.1.3 dev: true + /entities/4.4.0: + resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + engines: {node: '>=0.12'} + dev: true + /envinfo/7.8.1: resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} engines: {node: '>=4'} @@ -8495,6 +9209,10 @@ packages: is-symbol: 1.0.4 dev: true + /es6-promise/3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + /esbuild-android-64/0.15.9: resolution: {integrity: sha512-HQCX7FJn9T4kxZQkhPjNZC7tBWZqJvhlLHPU2SFzrQB/7nDXjmTIFpFTjt7Bd1uFpeXmuwf5h5fZm+x/hLnhbw==} engines: {node: '>=12'} @@ -8705,6 +9423,36 @@ packages: esbuild-windows-arm64: 0.15.9 dev: true + /esbuild/0.16.15: + resolution: {integrity: sha512-v+3ozjy9wyj8cOElzx3//Lsb4TCxPfZxRmdsfm0YaEkvZu7y6rKH7Zi1UpDx4JI7dSQui+U1Qxhfij9KBbHfrA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.15 + '@esbuild/android-arm64': 0.16.15 + '@esbuild/android-x64': 0.16.15 + '@esbuild/darwin-arm64': 0.16.15 + '@esbuild/darwin-x64': 0.16.15 + '@esbuild/freebsd-arm64': 0.16.15 + '@esbuild/freebsd-x64': 0.16.15 + '@esbuild/linux-arm': 0.16.15 + '@esbuild/linux-arm64': 0.16.15 + '@esbuild/linux-ia32': 0.16.15 + '@esbuild/linux-loong64': 0.16.15 + '@esbuild/linux-mips64el': 0.16.15 + '@esbuild/linux-ppc64': 0.16.15 + '@esbuild/linux-riscv64': 0.16.15 + '@esbuild/linux-s390x': 0.16.15 + '@esbuild/linux-x64': 0.16.15 + '@esbuild/netbsd-x64': 0.16.15 + '@esbuild/openbsd-x64': 0.16.15 + '@esbuild/sunos-x64': 0.16.15 + '@esbuild/win32-arm64': 0.16.15 + '@esbuild/win32-ia32': 0.16.15 + '@esbuild/win32-x64': 0.16.15 + dev: true + /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -9159,6 +9907,31 @@ packages: eslint: 7.32.0 dev: true + /eslint-plugin-svelte/2.14.1_svelte@3.55.0: + resolution: {integrity: sha512-7M4QHtbtTjLA2xore4rXBwKshPaycil5AsOwYNyvJdunEEdimrIp6otX6PGpFoAojz+qTb4MZuReaHEj1hX7Wg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 + svelte: ^3.37.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + debug: 4.3.4 + eslint-utils: 3.0.0 + esutils: 2.0.3 + known-css-properties: 0.26.0 + postcss: 8.4.21 + postcss-load-config: 3.1.4_postcss@8.4.21 + postcss-safe-parser: 6.0.0_postcss@8.4.21 + sourcemap-codec: 1.4.8 + svelte: 3.55.0 + svelte-eslint-parser: 0.22.3_svelte@3.55.0 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /eslint-restricted-globals/0.2.0: resolution: {integrity: sha512-kwYJALm5KS2QW3Mc1PgObO4V+pTR6RQtRT65L1GQILlEnAhabUQqGAX7/qUjoQR4KZJKehWpBtyDEiDecwmY9A==} dev: true @@ -9186,6 +9959,15 @@ packages: eslint-visitor-keys: 1.3.0 dev: true + /eslint-utils/3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-utils/3.0.0_eslint@7.32.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} @@ -9318,6 +10100,10 @@ packages: - supports-color dev: true + /esm-env/1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + dev: true + /espree/7.3.1: resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} engines: {node: ^10.12.0 || >=12.0.0} @@ -9331,8 +10117,8 @@ packages: resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.8.0 - acorn-jsx: 5.3.2_acorn@8.8.0 + acorn: 8.8.1 + acorn-jsx: 5.3.2_acorn@8.8.1 eslint-visitor-keys: 3.3.0 dev: true @@ -9365,10 +10151,6 @@ packages: engines: {node: '>=4.0'} dev: true - /estree-walker/0.6.1: - resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} - dev: true - /estree-walker/1.0.1: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} dev: true @@ -9548,7 +10330,7 @@ packages: dependencies: chalk: 4.1.2 commander: 7.2.0 - fast-glob: 3.2.11 + fast-glob: 3.2.12 find-up: 5.0.0 fs-extra: 9.1.0 dev: false @@ -9651,6 +10433,17 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 + dev: true + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -9862,6 +10655,19 @@ packages: combined-stream: 1.0.8 mime-types: 2.1.35 + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + /fragment-cache/0.2.1: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} @@ -9946,6 +10752,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-func-name/2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + /get-intrinsic/1.1.2: resolution: {integrity: sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==} dependencies: @@ -10058,18 +10868,26 @@ packages: type-fest: 0.20.2 dev: true + /globalyzer/0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.2.11 + fast-glob: 3.2.12 ignore: 5.2.0 merge2: 1.4.1 slash: 3.0.0 dev: true + /globrex/0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + /goober/2.1.10: resolution: {integrity: sha512-7PpuQMH10jaTWm33sQgBQvz45pHR8N4l3Cu3WMGEWmHShAcTuuP7I+5/DwKo39fwti5A80WAjvqgz6SSlgWmGA==} peerDependencies: @@ -10257,6 +11075,13 @@ packages: whatwg-encoding: 1.0.5 dev: true + /html-encoding-sniffer/3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + /html-entities/2.3.2: resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==} dev: true @@ -10287,6 +11112,17 @@ packages: - supports-color dev: true + /http-proxy-agent/5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /https-proxy-agent/5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -10317,7 +11153,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 - dev: false /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -10367,6 +11202,10 @@ packages: resolve-cwd: 3.0.0 dev: true + /import-meta-resolve/2.2.1: + resolution: {integrity: sha512-C6lLL7EJPY44kBvA80gq4uMsVFw5x3oSKfuMl1cuZ2RkI5+UJqQXgn+6hlUew0y4ig7Ypt4CObAAIzU53Nfpuw==} + dev: true + /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -10782,8 +11621,8 @@ packages: engines: {node: '>=8'} dev: true - /istanbul-lib-instrument/5.2.0: - resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} + /istanbul-lib-instrument/5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: '@babel/core': 7.19.1 @@ -10815,8 +11654,8 @@ packages: - supports-color dev: true - /istanbul-reports/3.1.4: - resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==} + /istanbul-reports/3.1.5: + resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} engines: {node: '>=8'} dependencies: html-escaper: 2.0.2 @@ -11504,12 +12343,12 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.8.0 + acorn: 8.8.1 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 data-urls: 2.0.0 - decimal.js: 10.3.1 + decimal.js: 10.4.3 domexception: 2.0.1 escodegen: 2.0.0 form-data: 3.0.1 @@ -11517,11 +12356,11 @@ packages: http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.1 + nwsapi: 2.2.2 parse5: 6.0.1 saxes: 5.0.1 symbol-tree: 3.2.4 - tough-cookie: 4.0.0 + tough-cookie: 4.1.2 w3c-hr-time: 1.0.2 w3c-xmlserializer: 2.0.0 webidl-conversions: 6.1.0 @@ -11536,6 +12375,47 @@ packages: - utf-8-validate dev: true + /jsdom/20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.8.1 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.4.3 + domexception: 4.0.0 + escodegen: 2.0.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.2 + parse5: 7.1.2 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.2 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.11.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc/0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -11580,6 +12460,10 @@ packages: engines: {node: '>=6'} hasBin: true + /jsonc-parser/3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + /jsonfile/2.4.0: resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} optionalDependencies: @@ -11719,6 +12603,15 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + /kleur/4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /known-css-properties/0.26.0: + resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==} + dev: true + /ky-universal/0.8.2_53xdiffegfcxt6522645rot5ue: resolution: {integrity: sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==} engines: {node: '>=10.17'} @@ -11809,6 +12702,11 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /local-pkg/0.4.2: + resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} + engines: {node: '>=14'} + dev: true + /localforage/1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} dependencies: @@ -11935,6 +12833,18 @@ packages: dependencies: js-tokens: 4.0.0 + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + + /lower-case/2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.4.1 + dev: true + /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -11970,6 +12880,13 @@ packages: sourcemap-codec: 1.4.8 dev: true + /magic-string/0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /make-dir/2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -12512,6 +13429,12 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + /mime/3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: true + /mimic-fn/1.2.0: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} engines: {node: '>=4'} @@ -12574,10 +13497,29 @@ packages: dependencies: minimist: 1.2.6 + /mlly/1.1.0: + resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} + dependencies: + acorn: 8.8.1 + pathe: 1.0.0 + pkg-types: 1.0.1 + ufo: 1.0.1 + dev: true + /mockdate/3.0.5: resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==} dev: false + /mri/1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime/1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -12711,6 +13653,13 @@ packages: /nice-try/1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + /no-case/3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.4.1 + dev: true + /nocache/2.1.0: resolution: {integrity: sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==} engines: {node: '>=4.0.0'} @@ -12804,6 +13753,11 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + /npm-run-path/2.0.2: resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} @@ -12821,8 +13775,8 @@ packages: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} dev: false - /nwsapi/2.2.1: - resolution: {integrity: sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==} + /nwsapi/2.2.2: + resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==} dev: true /ob1/0.59.0: @@ -12845,6 +13799,11 @@ packages: define-property: 0.2.5 kind-of: 3.2.2 + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -13173,11 +14132,24 @@ packages: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} dev: true + /parse5/7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.4.0 + dev: true + /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} dev: false + /pascal-case/3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.4.1 + dev: true + /pascalcase/0.1.1: resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} engines: {node: '>=0.10.0'} @@ -13225,7 +14197,19 @@ packages: engines: {node: '>=8'} dev: true - /performance-now/2.1.0: + /pathe/0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + dev: true + + /pathe/1.0.0: + resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} + dev: true + + /pathval/1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /performance-now/2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} dev: false @@ -13240,6 +14224,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + /pify/4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -13268,6 +14257,14 @@ packages: find-up: 4.1.0 dev: true + /pkg-types/1.0.1: + resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.1.0 + pathe: 1.0.0 + dev: true + /pkg-up/3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} @@ -13299,6 +14296,28 @@ packages: resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} engines: {node: '>=0.10.0'} + /postcss-import/14.1.0_postcss@8.4.21: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + dev: true + + /postcss-js/4.0.0_postcss@8.4.21: + resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.3.3 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + dev: true + /postcss-load-config/3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -13315,6 +14334,54 @@ packages: yaml: 1.10.2 dev: true + /postcss-load-config/3.1.4_postcss@8.4.21: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + dev: true + + /postcss-nested/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-safe-parser/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-selector-parser/6.0.11: + resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + /postcss/8.4.16: resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==} engines: {node: ^10 || ^12 || >=14} @@ -13324,8 +14391,8 @@ packages: source-map-js: 1.0.2 dev: true - /postcss/8.4.18: - resolution: {integrity: sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==} + /postcss/8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.4 @@ -13358,6 +14425,15 @@ packages: fast-diff: 1.2.0 dev: true + /prettier-plugin-svelte/2.9.0_prettier@2.7.1: + resolution: {integrity: sha512-3doBi5NO4IVgaNPtwewvrgPpqAcvNv0NwJNflr76PIGgi9nf1oguQV1Hpdm9TI2ALIQVn/9iIwLpBO5UcD2Jiw==} + peerDependencies: + prettier: ^1.16.4 || ^2.0.0 + svelte: ^3.2.0 + dependencies: + prettier: 2.7.1 + dev: true + /prettier/2.7.1: resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} engines: {node: '>=10.13.0'} @@ -13467,7 +14543,6 @@ packages: /querystringify/2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - dev: false /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -13477,6 +14552,11 @@ packages: engines: {node: '>=8'} dev: true + /quick-lru/5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + /raf/3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} dependencies: @@ -13819,6 +14899,12 @@ packages: dependencies: loose-envify: 1.4.0 + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -13870,7 +14956,7 @@ packages: ast-types: 0.14.2 esprima: 4.0.1 source-map: 0.6.1 - tslib: 2.4.0 + tslib: 2.4.1 /rechoir/0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} @@ -13973,13 +15059,8 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: false - /require-relative/0.8.7: - resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} - dev: true - /requires-port/1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - dev: false /reselect/4.1.6: resolution: {integrity: sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==} @@ -14066,7 +15147,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: false /rimraf/3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -14083,19 +15163,6 @@ packages: - supports-color dev: true - /rollup-plugin-svelte/7.1.0_7rgskvp3hfjz55pue4td2a2xga: - resolution: {integrity: sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==} - engines: {node: '>=10'} - peerDependencies: - rollup: '>=2.0.0' - svelte: '>=3.5.0' - dependencies: - require-relative: 0.8.7 - rollup: 2.78.1 - rollup-pluginutils: 2.8.2 - svelte: 3.49.0 - dev: true - /rollup-plugin-terser/7.0.2_rollup@2.78.1: resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} peerDependencies: @@ -14122,12 +15189,6 @@ packages: yargs: 17.5.1 dev: true - /rollup-pluginutils/2.8.2: - resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} - dependencies: - estree-walker: 0.6.1 - dev: true - /rollup/2.78.1: resolution: {integrity: sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==} engines: {node: '>=10.0.0'} @@ -14144,6 +15205,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup/3.9.1: + resolution: {integrity: sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /rooks/6.4.3_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-nHrCdzlU8EAoDo1sNzLLzOSVgqK4ypbhOpLmbsEI7b4xDOagsiePueYwVJizGE4l5qhOYY5x+8YuSYZDp+OMjw==} engines: {node: '>=v10.24.1'} @@ -14175,7 +15244,14 @@ packages: /rxjs/7.5.5: resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 + + /sade/1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -14191,6 +15267,15 @@ packages: /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + /sander/0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + /sane/4.1.0: resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} engines: {node: 6.* || 8.* || >= 10.*} @@ -14220,6 +15305,13 @@ packages: xmlchars: 2.2.0 dev: true + /saxes/6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + /scheduler/0.20.2: resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} dependencies: @@ -14316,6 +15408,10 @@ packages: resolution: {integrity: sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==} dev: false + /set-cookie-parser/2.5.1: + resolution: {integrity: sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==} + dev: true + /set-value/2.0.1: resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} engines: {node: '>=0.10.0'} @@ -14391,6 +15487,10 @@ packages: object-inspect: 1.12.2 dev: true + /siginfo/2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -14406,6 +15506,15 @@ packages: dependencies: is-arrayish: 0.3.2 + /sirv/2.0.2: + resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.21 + mrmime: 1.0.1 + totalist: 3.0.0 + dev: true + /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -14534,6 +15643,16 @@ packages: solid-js: 1.5.7 dev: true + /sorcery/0.10.0: + resolution: {integrity: sha512-R5ocFmKZQFfSTstfOtHjJuAwbpGyf9qjQa1egyhvXSbM7emjrtLXtGdZsDJDABC85YBfVvrOiGWKSYXPKdvP1g==} + hasBin: true + dependencies: + buffer-crc32: 0.2.13 + minimist: 1.2.6 + sander: 0.5.1 + sourcemap-codec: 1.4.8 + dev: true + /sort-by/1.2.0: resolution: {integrity: sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==} dependencies: @@ -14593,6 +15712,7 @@ packages: /sourcemap-codec/1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead /spawn-command/0.0.2-1: resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==} @@ -14657,6 +15777,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback/0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + /stackframe/1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} dev: false @@ -14702,6 +15826,11 @@ packages: any-promise: 1.3.0 dev: true + /streamsearch/1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: true + /strict-event-emitter/0.2.4: resolution: {integrity: sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ==} dependencies: @@ -14820,6 +15949,12 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal/1.0.0: + resolution: {integrity: sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==} + dependencies: + acorn: 8.8.1 + dev: true + /styled-jsx/5.0.2_react@18.2.0: resolution: {integrity: sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==} engines: {node: '>= 12.0.0'} @@ -14890,11 +16025,203 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /svelte/3.49.0: - resolution: {integrity: sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==} + /svelte-check/2.10.3_77wbasr76lhjripnylrva3hecy: + resolution: {integrity: sha512-Nt1aWHTOKFReBpmJ1vPug0aGysqPwJh2seM1OvICfM2oeyaA62mOiy5EvkXhltGfhCcIQcq2LoE0l1CwcWPjlw==} + hasBin: true + peerDependencies: + svelte: ^3.24.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.14 + chokidar: 3.5.3 + fast-glob: 3.2.11 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 3.55.0 + svelte-preprocess: 4.10.7_e2sshb7keot2ipsq5ilqqjh4f4 + typescript: 4.8.4 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - node-sass + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-check/2.10.3_svelte@3.55.0: + resolution: {integrity: sha512-Nt1aWHTOKFReBpmJ1vPug0aGysqPwJh2seM1OvICfM2oeyaA62mOiy5EvkXhltGfhCcIQcq2LoE0l1CwcWPjlw==} + hasBin: true + peerDependencies: + svelte: ^3.24.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.14 + chokidar: 3.5.3 + fast-glob: 3.2.11 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 3.55.0 + svelte-preprocess: 4.10.7_glsdxddlaertg66rhhvanbinpy + typescript: 4.8.4 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - node-sass + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-eslint-parser/0.22.3_svelte@3.55.0: + resolution: {integrity: sha512-l9M1QbQ8YsF92FNtwHYKoJWnJvBAKB89jmiKLCG9R5GOlidehFzvmxzdK4lsJjzx5UylrTKuKlR815RFopq1Vw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 + dependencies: + eslint-scope: 7.1.1 + eslint-visitor-keys: 3.3.0 + espree: 9.4.0 + svelte: 3.55.0 + dev: true + + /svelte-hmr/0.15.1_svelte@3.55.0: + resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: '>=3.19.0' + dependencies: + svelte: 3.55.0 + dev: true + + /svelte-preprocess/4.10.7_e2sshb7keot2ipsq5ilqqjh4f4: + resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} + engines: {node: '>= 9.11.2'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + node-sass: '*' + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + node-sass: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + '@types/sass': 1.43.1 + detect-indent: 6.1.0 + magic-string: 0.25.9 + postcss: 8.4.21 + sorcery: 0.10.0 + strip-indent: 3.0.0 + svelte: 3.55.0 + typescript: 4.8.4 + dev: true + + /svelte-preprocess/4.10.7_glsdxddlaertg66rhhvanbinpy: + resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} + engines: {node: '>= 9.11.2'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + node-sass: '*' + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + node-sass: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + '@types/sass': 1.43.1 + detect-indent: 6.1.0 + magic-string: 0.25.9 + sorcery: 0.10.0 + strip-indent: 3.0.0 + svelte: 3.55.0 + typescript: 4.8.4 + dev: true + + /svelte/3.55.0: + resolution: {integrity: sha512-uGu2FVMlOuey4JoKHKrpZFkoYyj0VLjJdz47zX5+gVK5odxHM40RVhar9/iK2YFRVxvfg9FkhfVlR0sjeIrOiA==} engines: {node: '>= 8'} dev: true + /svelte2tsx/0.6.0_glsdxddlaertg66rhhvanbinpy: + resolution: {integrity: sha512-TrxfQkO7CKi8Pu2eC/FyteDCdk3OOeQV5u6z7OjYAsOhsd0ClzAKqxJdvp6xxNQLrbFzf/XvCi9Fy8MQ1MleFA==} + peerDependencies: + svelte: ^3.55 + typescript: ^4.9.4 + dependencies: + dedent-js: 1.0.1 + pascal-case: 3.1.2 + svelte: 3.55.0 + typescript: 4.8.4 + dev: true + /symbol-tree/3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true @@ -14910,6 +16237,40 @@ packages: strip-ansi: 6.0.1 dev: true + /tailwindcss/3.2.4_postcss@8.4.21: + resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==} + engines: {node: '>=12.13.0'} + hasBin: true + peerDependencies: + postcss: ^8.0.9 + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-import: 14.1.0_postcss@8.4.21 + postcss-js: 4.0.0_postcss@8.4.21 + postcss-load-config: 3.1.4_postcss@8.4.21 + postcss-nested: 6.0.0_postcss@8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + dev: true + /tapable/1.1.3: resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} engines: {node: '>=6'} @@ -15018,6 +16379,13 @@ packages: resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==} dev: true + /tiny-glob/0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + /tiny-invariant/1.2.0: resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} dev: false @@ -15026,10 +16394,24 @@ packages: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} dev: false + /tinybench/2.3.1: + resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} + dev: true + /tinycolor2/1.4.2: resolution: {integrity: sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==} dev: true + /tinypool/0.3.0: + resolution: {integrity: sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy/1.0.2: + resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} + engines: {node: '>=14.0.0'} + dev: true + /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -15077,13 +16459,19 @@ packages: engines: {node: '>=0.6'} dev: false - /tough-cookie/4.0.0: - resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} + /totalist/3.0.0: + resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} + engines: {node: '>=6'} + dev: true + + /tough-cookie/4.1.2: + resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} dependencies: psl: 1.9.0 punycode: 2.1.1 - universalify: 0.1.2 + universalify: 0.2.0 + url-parse: 1.5.10 dev: true /tr46/0.0.3: @@ -15102,6 +16490,13 @@ packages: punycode: 2.1.1 dev: true + /tr46/3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.1.1 + dev: true + /traverse/0.6.6: resolution: {integrity: sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==} dev: true @@ -15203,8 +16598,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib/2.4.0: - resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} /tsup/6.3.0: resolution: {integrity: sha512-IaNQO/o1rFgadLhNonVKNCT2cks+vvnWX3DnL8sB87lBDqRvJXHENr5lSPJlqwplUlDxSwZK8dSg87rgBu6Emw==} @@ -15355,6 +16750,10 @@ packages: resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==} dev: false + /ufo/1.0.1: + resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==} + dev: true + /uglify-es/3.3.9: resolution: {integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==} engines: {node: '>=0.8.0'} @@ -15378,6 +16777,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici/5.14.0: + resolution: {integrity: sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + dev: true + /unfetch/4.1.0: resolution: {integrity: sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==} dev: false @@ -15421,6 +16827,11 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + /universalify/0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify/1.0.0: resolution: {integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==} engines: {node: '>= 10.0.0'} @@ -15473,7 +16884,6 @@ packages: dependencies: querystringify: 2.2.0 requires-port: 1.0.0 - dev: false /use-subscription/1.1.1_react@17.0.1: resolution: {integrity: sha512-gk4fPTYvNhs6Ia7u8/+K7bM7sZ7O7AMfWtS+zPO8luH+zWuiGgGcrW0hL4MRWZSzXo+4ofNorf87wZwBKz2YdQ==} @@ -15559,6 +16969,29 @@ packages: engines: {node: '>= 0.8'} dev: false + /vite-node/0.27.1_@types+node@17.0.45: + resolution: {integrity: sha512-d6+ue/3NzsfndWaPbYh/bFkHbmAWfDXI4B874zRx+WREnG6CUHUbBC8lKaRYZjeR6gCPN5m1aVNNRXBYICA9XA==} + engines: {node: '>=v14.16.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.1.0 + pathe: 0.2.0 + picocolors: 1.0.0 + source-map: 0.6.1 + source-map-support: 0.5.21 + vite: 4.0.4_@types+node@17.0.45 + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite-plugin-solid/2.3.9_solid-js@1.5.4+vite@3.1.3: resolution: {integrity: sha512-+lprsYgt9DVNp0kbDj2d2HWAPI13L8ff5xslk9SjiPBcsY/YUZ/1Wj0J/Oj5aiVAhwfPm8IcM3bzyHJUPlmc8w==} peerDependencies: @@ -15650,7 +17083,7 @@ packages: optional: true dependencies: esbuild: 0.15.9 - postcss: 8.4.16 + postcss: 8.4.21 resolve: 1.22.1 rollup: 2.78.1 optionalDependencies: @@ -15680,13 +17113,141 @@ packages: optional: true dependencies: esbuild: 0.15.9 - postcss: 8.4.18 + postcss: 8.4.21 resolve: 1.22.1 rollup: 2.79.1 optionalDependencies: fsevents: 2.3.2 dev: true + /vite/4.0.4: + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.16.15 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.9.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vite/4.0.4_@types+node@17.0.45: + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 17.0.45 + esbuild: 0.16.15 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.9.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitefu/0.2.4_vite@4.0.4: + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.0.4 + dev: true + + /vitest/0.27.1_jsdom@20.0.3: + resolution: {integrity: sha512-1sIpQ1DVFTEn7c1ici1XHcVfdU4nKiBmPtPAtGKJJJLuJjojTv/OHGgcf69P57alM4ty8V4NMv+7Yoi5Cxqx9g==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.4 + '@types/chai-subset': 1.3.3 + '@types/node': 17.0.45 + acorn: 8.8.1 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4 + jsdom: 20.0.3 + local-pkg: 0.4.2 + picocolors: 1.0.0 + source-map: 0.6.1 + strip-literal: 1.0.0 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 4.0.4_@types+node@17.0.45 + vite-node: 0.27.1_@types+node@17.0.45 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vlq/1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} dev: false @@ -15764,6 +17325,13 @@ packages: xml-name-validator: 3.0.0 dev: true + /w3c-xmlserializer/4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + /walker/1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: @@ -15801,12 +17369,24 @@ packages: engines: {node: '>=10.4'} dev: true + /webidl-conversions/7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + /whatwg-encoding/1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} dependencies: iconv-lite: 0.4.24 dev: true + /whatwg-encoding/2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + /whatwg-fetch/3.0.0: resolution: {integrity: sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==} dev: false @@ -15815,6 +17395,19 @@ packages: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} dev: true + /whatwg-mimetype/3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url/11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + /whatwg-url/5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -15866,6 +17459,15 @@ packages: isexe: 2.0.0 dev: true + /why-is-node-running/2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -15948,6 +17550,19 @@ packages: utf-8-validate: optional: true + /ws/8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /xcode/2.1.0: resolution: {integrity: sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ==} engines: {node: '>=6.0.0'} @@ -15983,6 +17598,11 @@ packages: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} dev: true + /xml-name-validator/4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + /xml-parse-from-string/1.0.1: resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==} dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ecc7b6443e..ff82673364 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - 'packages/**' - 'examples/react/**' - 'examples/solid/**' + - 'examples/svelte/**' - 'examples/vue/**' - '!examples/vue/2*' - '!examples/vue/nuxt*' diff --git a/scripts/config.ts b/scripts/config.ts index 830266c069..7156272fdc 100644 --- a/scripts/config.ts +++ b/scripts/config.ts @@ -40,6 +40,11 @@ export const packages: Package[] = [ packageDir: 'solid-query', srcDir: 'src', }, + { + name: '@tanstack/svelte-query', + packageDir: 'svelte-query', + srcDir: 'src', + }, { name: '@tanstack/vue-query', packageDir: 'vue-query', diff --git a/scripts/publish.ts b/scripts/publish.ts index c582d6e240..57d30b1148 100644 --- a/scripts/publish.ts +++ b/scripts/publish.ts @@ -388,6 +388,8 @@ async function run() { const entries = pkg.name === '@tanstack/eslint-plugin-query' ? (['main'] as const) + : pkg.name === '@tanstack/svelte-query' + ? (['types', 'module'] as const) : (['main', 'types', 'module'] as const) await Promise.all( From 2505f38712b42f65ffa43da4038fd2f25cd699f8 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 25 Jan 2023 08:45:33 +0100 Subject: [PATCH 025/314] chore: remove unused test helpers from svelte-query --- packages/svelte-query/src/__tests__/utils.ts | 27 +------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/packages/svelte-query/src/__tests__/utils.ts b/packages/svelte-query/src/__tests__/utils.ts index bbdcf88a96..2ccf931daf 100644 --- a/packages/svelte-query/src/__tests__/utils.ts +++ b/packages/svelte-query/src/__tests__/utils.ts @@ -1,10 +1,6 @@ import { vi } from 'vitest' import { act } from '@testing-library/svelte' -import { - QueryClient, - type QueryClientConfig, - type MutationOptions, -} from '../index' +import { QueryClient, type QueryClientConfig } from '../index' export function createQueryClient(config?: QueryClientConfig): QueryClient { vi.spyOn(console, 'error').mockImplementation(() => undefined) @@ -49,24 +45,3 @@ export function setActTimeout(fn: () => void, ms?: number) { }) }, ms) } - -/** - * Assert the parameter is of a specific type. - */ -export function expectType(_: T): void { - return undefined -} - -/** - * Assert the parameter is not typed as `any` - */ -export function expectTypeNotAny(_: 0 extends 1 & T ? never : T): void { - return undefined -} - -export function executeMutation( - queryClient: QueryClient, - options: MutationOptions, -): Promise { - return queryClient.getMutationCache().build(queryClient, options).execute() -} From 6eac22faabee924dab9bb7f41b8bf7b1a92d3fca Mon Sep 17 00:00:00 2001 From: Saurabh Bhan <1729481+saurabhan@users.noreply.github.com> Date: Thu, 26 Jan 2023 00:27:50 +0530 Subject: [PATCH 026/314] refactor: removed custom Logger #4675 (#4721) * Removed Custom Logger * [ref] added consoleMock in tests * chore: remove replace plugin for cjs becaus there is nothing left to replace * fix: remove logging of erroneous queries * test: fix some tests * test: fix query-core and react-query tests * test: fixed tests and removed consoleMock from some tests * chore: fixed linting and format * test: removed unnessecary consoleMocks and conditional expect * test: removed consoleMock and expect * test: remove test that doesn't make much sense anymore * chore: remove logger from svelte test helper Co-authored-by: Dominik Dorfmeister --- packages/query-core/src/index.ts | 1 - packages/query-core/src/logger.native.ts | 11 ---- packages/query-core/src/logger.ts | 9 --- packages/query-core/src/mutation.ts | 9 --- packages/query-core/src/mutationCache.ts | 1 - packages/query-core/src/query.ts | 17 ++---- packages/query-core/src/queryCache.ts | 1 - packages/query-core/src/queryClient.ts | 14 ----- packages/query-core/src/queryObserver.ts | 6 -- .../src/tests/queriesObserver.test.tsx | 14 ++--- packages/query-core/src/tests/query.test.tsx | 14 +++-- .../query-core/src/tests/queryClient.test.tsx | 3 +- .../src/tests/queryObserver.test.tsx | 55 +------------------ packages/query-core/src/tests/utils.ts | 9 +-- packages/query-core/src/types.ts | 2 - .../src/__tests__/utils.ts | 9 +-- .../query-persist-client-core/src/persist.ts | 10 ++-- .../PersistQueryClientProvider.test.tsx | 15 +++-- .../src/__tests__/utils.ts | 9 +-- .../QueryResetErrorBoundary.test.tsx | 53 ++++++++++++++++++ .../src/__tests__/suspense.test.tsx | 49 +++++++++++++++++ .../src/__tests__/useMutation.test.tsx | 13 +++++ .../src/__tests__/useQueries.test.tsx | 8 +++ .../src/__tests__/useQuery.test.tsx | 17 ++++-- packages/react-query/src/__tests__/utils.tsx | 9 +-- .../src/__tests__/createQuery.test.tsx | 5 -- packages/solid-query/src/__tests__/utils.tsx | 9 +-- packages/svelte-query/src/__tests__/utils.ts | 3 +- .../vue-query/src/__mocks__/useQueryClient.ts | 6 -- packages/vue-query/src/queryClient.ts | 1 - rollup.config.ts | 21 ++----- 31 files changed, 182 insertions(+), 221 deletions(-) delete mode 100644 packages/query-core/src/logger.native.ts delete mode 100644 packages/query-core/src/logger.ts diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index 1953ff9d2b..c2c2c07286 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -25,7 +25,6 @@ export { export * from './types' export type { Query, QueryState } from './query' export type { Mutation } from './mutation' -export type { Logger } from './logger' export type { DehydrateOptions, DehydratedState, diff --git a/packages/query-core/src/logger.native.ts b/packages/query-core/src/logger.native.ts deleted file mode 100644 index 161b5b72f4..0000000000 --- a/packages/query-core/src/logger.native.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Logger } from './logger' - -/** - * See https://github.com/tannerlinsley/react-query/issues/795 - * and https://github.com/tannerlinsley/react-query/pull/3246/#discussion_r795105707 - */ -export const defaultLogger: Logger = { - log: console.log, - warn: console.warn, - error: console.warn, -} diff --git a/packages/query-core/src/logger.ts b/packages/query-core/src/logger.ts deleted file mode 100644 index 8025b966b1..0000000000 --- a/packages/query-core/src/logger.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface Logger { - log: LogFunction - warn: LogFunction - error: LogFunction -} - -type LogFunction = (...args: any[]) => void - -export const defaultLogger: Logger = console diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index e6404aba58..62dd4d1422 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -1,8 +1,6 @@ import type { MutationOptions, MutationStatus, MutationMeta } from './types' import type { MutationCache } from './mutationCache' import type { MutationObserver } from './mutationObserver' -import type { Logger } from './logger' -import { defaultLogger } from './logger' import { notifyManager } from './notifyManager' import { Removable } from './removable' import type { Retryer } from './retryer' @@ -14,7 +12,6 @@ interface MutationConfig { mutationId: number mutationCache: MutationCache options: MutationOptions - logger?: Logger defaultOptions?: MutationOptions state?: MutationState meta?: MutationMeta @@ -88,7 +85,6 @@ export class Mutation< #observers: MutationObserver[] #mutationCache: MutationCache - #logger: Logger #retryer?: Retryer constructor(config: MutationConfig) { @@ -100,7 +96,6 @@ export class Mutation< } this.mutationId = config.mutationId this.#mutationCache = config.mutationCache - this.#logger = config.logger || defaultLogger this.#observers = [] this.state = config.state || getDefaultState() @@ -236,10 +231,6 @@ export class Mutation< this as Mutation, ) - if (process.env.NODE_ENV !== 'production') { - this.#logger.error(error) - } - await this.options.onError?.( error as TError, this.state.variables!, diff --git a/packages/query-core/src/mutationCache.ts b/packages/query-core/src/mutationCache.ts index 3f2bf11540..91e135c3f5 100644 --- a/packages/query-core/src/mutationCache.ts +++ b/packages/query-core/src/mutationCache.ts @@ -91,7 +91,6 @@ export class MutationCache extends Subscribable { ): Mutation { const mutation = new Mutation({ mutationCache: this, - logger: client.getLogger(), mutationId: ++this.#mutationId, options: client.defaultMutationOptions(options), state, diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 8049a96519..7c378d1cb6 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -12,8 +12,6 @@ import type { } from './types' import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' -import type { Logger } from './logger' -import { defaultLogger } from './logger' import { notifyManager } from './notifyManager' import type { Retryer } from './retryer' import { isCancelledError, canFetch, createRetryer } from './retryer' @@ -30,7 +28,6 @@ interface QueryConfig< cache: QueryCache queryKey: TQueryKey queryHash: string - logger?: Logger options?: QueryOptions defaultOptions?: QueryOptions state?: QueryState @@ -153,7 +150,6 @@ export class Query< #initialState: QueryState #revertState?: QueryState #cache: QueryCache - #logger: Logger #promise?: Promise #retryer?: Retryer #observers: QueryObserver[] @@ -168,7 +164,6 @@ export class Query< this.#setOptions(config.options) this.#observers = [] this.#cache = config.cache - this.#logger = config.logger || defaultLogger this.queryKey = config.queryKey this.queryHash = config.queryHash this.#initialState = config.state || getDefaultState(this.options) @@ -354,9 +349,9 @@ export class Query< } } - if (!Array.isArray(this.options.queryKey)) { - if (process.env.NODE_ENV !== 'production') { - this.#logger.error( + if (process.env.NODE_ENV !== 'production') { + if (!Array.isArray(this.options.queryKey)) { + console.error( `As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']`, ) } @@ -438,10 +433,6 @@ export class Query< if (!isCancelledError(error)) { // Notify cache callback this.#cache.config.onError?.(error, this as Query) - - if (process.env.NODE_ENV !== 'production') { - this.#logger.error(error) - } } if (!this.isFetchingOptimistic) { @@ -458,7 +449,7 @@ export class Query< onSuccess: (data) => { if (typeof data === 'undefined') { if (process.env.NODE_ENV !== 'production') { - this.#logger.error( + console.error( `Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ${this.queryHash}`, ) } diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index 4b827fcf67..0a38ec58fe 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -87,7 +87,6 @@ export class QueryCache extends Subscribable { if (!query) { query = new Query({ cache: this, - logger: client.getLogger(), queryKey, queryHash, options: client.defaultQueryOptions(options), diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 0585aa4799..c60ba5033a 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -34,8 +34,6 @@ import { onlineManager } from './onlineManager' import { notifyManager } from './notifyManager' import { infiniteQueryBehavior } from './infiniteQueryBehavior' import type { CancelOptions, DefaultedQueryObserverOptions } from './types' -import type { Logger } from './logger' -import { defaultLogger } from './logger' // TYPES @@ -54,7 +52,6 @@ interface MutationDefaults { export class QueryClient { #queryCache: QueryCache #mutationCache: MutationCache - #logger: Logger #defaultOptions: DefaultOptions #queryDefaults: Map #mutationDefaults: Map @@ -65,17 +62,10 @@ export class QueryClient { constructor(config: QueryClientConfig = {}) { this.#queryCache = config.queryCache || new QueryCache() this.#mutationCache = config.mutationCache || new MutationCache() - this.#logger = config.logger || defaultLogger this.#defaultOptions = config.defaultOptions || {} this.#queryDefaults = new Map() this.#mutationDefaults = new Map() this.#mountCount = 0 - - if (process.env.NODE_ENV !== 'production' && config.logger) { - this.#logger.error( - `Passing a custom logger has been deprecated and will be removed in the next major version.`, - ) - } } mount(): void { @@ -352,10 +342,6 @@ export class QueryClient { return this.#mutationCache } - getLogger(): Logger { - return this.#logger - } - getDefaultOptions(): DefaultOptions { return this.#defaultOptions } diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 66d1a421ef..f5ecc0bda6 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -466,9 +466,6 @@ export class QueryObserver< this.#selectResult = data this.#selectError = null } catch (selectError) { - if (process.env.NODE_ENV !== 'production') { - this.#client.getLogger().error(selectError) - } this.#selectError = selectError as TError } } @@ -504,9 +501,6 @@ export class QueryObserver< placeholderData = options.select(placeholderData) this.#selectError = null } catch (selectError) { - if (process.env.NODE_ENV !== 'production') { - this.#client.getLogger().error(selectError) - } this.#selectError = selectError as TError } } diff --git a/packages/query-core/src/tests/queriesObserver.test.tsx b/packages/query-core/src/tests/queriesObserver.test.tsx index 32487cd253..af2065f036 100644 --- a/packages/query-core/src/tests/queriesObserver.test.tsx +++ b/packages/query-core/src/tests/queriesObserver.test.tsx @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react' -import { sleep, queryKey, createQueryClient, mockLogger } from './utils' +import { sleep, queryKey, createQueryClient } from './utils' import type { QueryClient, QueryObserverResult } from '..' import { QueriesObserver } from '..' @@ -34,6 +34,8 @@ describe('queriesObserver', () => { }) test('should still return value for undefined query key', async () => { + const consoleMock = jest.spyOn(console, 'error') + consoleMock.mockImplementation(() => undefined) const key1 = queryKey() const queryFn1 = jest.fn().mockReturnValue(1) const queryFn2 = jest.fn().mockReturnValue(2) @@ -49,13 +51,11 @@ describe('queriesObserver', () => { unsubscribe() expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }]) - expect(mockLogger.error).toHaveBeenCalledTimes(2) - expect(mockLogger.error).toHaveBeenCalledWith( - 'Passing a custom logger has been deprecated and will be removed in the next major version.', - ) - expect(mockLogger.error).toHaveBeenCalledWith( - 'Passing a custom logger has been deprecated and will be removed in the next major version.', + expect(consoleMock).toHaveBeenCalledTimes(1) + expect(consoleMock).toHaveBeenCalledWith( + "As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']", ) + consoleMock.mockRestore() }) test('should update when a query updates', async () => { diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 7af305d964..8977bb9ca6 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -2,7 +2,6 @@ import { sleep, queryKey, mockVisibilityState, - mockLogger, createQueryClient, } from './utils' import type { @@ -761,12 +760,16 @@ describe('query', () => { const unsubscribe = observer.subscribe(() => undefined) await sleep(10) - expect(mockLogger.error).toHaveBeenCalledWith(new Error('Missing queryFn')) - + expect(observer.getCurrentResult()).toMatchObject({ + status: 'error', + error: new Error('Missing queryFn'), + }) unsubscribe() }) test('fetch should dispatch an error if the queryFn returns undefined', async () => { + const consoleMock = jest.spyOn(console, 'error') + consoleMock.mockImplementation(() => undefined) const key = queryKey() const observer = new QueryObserver(queryClient, { @@ -790,8 +793,11 @@ describe('query', () => { error, }) - expect(mockLogger.error).toHaveBeenCalledWith(error) + expect(consoleMock).toHaveBeenCalledWith( + `Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ["${key}"]`, + ) unsubscribe() + consoleMock.mockRestore() }) test('constructor should call initialDataUpdatedAt if defined as a function', async () => { diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index bb7906e24b..fb1e161775 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -1,7 +1,7 @@ import { waitFor } from '@testing-library/react' import '@testing-library/jest-dom' -import { sleep, queryKey, mockLogger, createQueryClient } from './utils' +import { sleep, queryKey, createQueryClient } from './utils' import type { QueryCache, QueryClient, @@ -669,7 +669,6 @@ describe('queryClient', () => { }) expect(result).toBeUndefined() - expect(mockLogger.error).toHaveBeenCalled() }) test('should be garbage collected after gcTime if unused', async () => { diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index 2e3458883d..16ec9f9960 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -1,10 +1,4 @@ -import { - sleep, - queryKey, - expectType, - mockLogger, - createQueryClient, -} from './utils' +import { sleep, queryKey, expectType, createQueryClient } from './utils' import type { QueryClient, QueryObserverResult } from '..' import { QueryObserver, focusManager } from '..' @@ -464,7 +458,7 @@ describe('queryObserver', () => { const fetchData = () => { count++ - Promise.resolve('data') + return Promise.resolve('data') } const observer = new QueryObserver(queryClient, { queryKey: key, @@ -527,36 +521,6 @@ describe('queryObserver', () => { expect(firstData).toBe(secondData) }) - test('the retrier should not throw an error when reject if the retrier is already resolved', async () => { - const key = queryKey() - let count = 0 - - const observer = new QueryObserver(queryClient, { - queryKey: key, - queryFn: () => { - count++ - return Promise.reject(`reject ${count}`) - }, - retry: 1, - retryDelay: 20, - }) - - const unsubscribe = observer.subscribe(() => undefined) - - // Simulate a race condition when an unsubscribe and a retry occur. - await sleep(20) - unsubscribe() - - // A second reject is triggered for the retry - // but the retryer has already set isResolved to true - // so it does nothing and no error is thrown - - // Should not log an error - queryClient.clear() - await sleep(40) - expect(mockLogger.error).not.toHaveBeenNthCalledWith(1, 'reject 1') - }) - test('should throw an error if enabled option type is not valid', async () => { const key = queryKey() @@ -653,21 +617,6 @@ describe('queryObserver', () => { unsubscribe() }) - test('select function error using placeholderdata should log an error', () => { - const key = queryKey() - - new QueryObserver(queryClient, { - queryKey: key, - queryFn: () => 'data', - placeholderData: 'placeholderdata', - select: () => { - throw new Error('error') - }, - }) - - expect(mockLogger.error).toHaveBeenNthCalledWith(2, new Error('error')) - }) - test('should not use replaceEqualDeep for select value when structuralSharing option is true and placeholderdata is defined', () => { const key = queryKey() diff --git a/packages/query-core/src/tests/utils.ts b/packages/query-core/src/tests/utils.ts index 9659fb1683..438510b7bc 100644 --- a/packages/query-core/src/tests/utils.ts +++ b/packages/query-core/src/tests/utils.ts @@ -5,8 +5,7 @@ import { QueryClient } from '@tanstack/query-core' import * as utils from '../utils' export function createQueryClient(config?: QueryClientConfig): QueryClient { - jest.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) + return new QueryClient(config) } export function mockVisibilityState(value: DocumentVisibilityState) { @@ -17,12 +16,6 @@ export function mockNavigatorOnLine(value: boolean) { return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } -export const mockLogger = { - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), -} - let queryKeyCount = 0 export function queryKey(): Array { queryKeyCount++ diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 1019202c4a..6ba53d6c12 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -6,7 +6,6 @@ import type { RetryValue, RetryDelayValue } from './retryer' import type { QueryFilters, QueryTypeFilter } from './utils' import type { QueryCache } from './queryCache' import type { MutationCache } from './mutationCache' -import type { Logger } from './logger' export type QueryKey = readonly unknown[] @@ -709,7 +708,6 @@ export type MutationObserverResult< export interface QueryClientConfig { queryCache?: QueryCache mutationCache?: MutationCache - logger?: Logger defaultOptions?: DefaultOptions } diff --git a/packages/query-persist-client-core/src/__tests__/utils.ts b/packages/query-persist-client-core/src/__tests__/utils.ts index 45654e329e..1668145889 100644 --- a/packages/query-persist-client-core/src/__tests__/utils.ts +++ b/packages/query-persist-client-core/src/__tests__/utils.ts @@ -2,14 +2,7 @@ import type { QueryClientConfig } from '@tanstack/query-core' import { QueryClient } from '@tanstack/query-core' export function createQueryClient(config?: QueryClientConfig): QueryClient { - jest.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) -} - -export const mockLogger = { - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), + return new QueryClient(config) } export function sleep(timeout: number): Promise { diff --git a/packages/query-persist-client-core/src/persist.ts b/packages/query-persist-client-core/src/persist.ts index 48a5ccb2ac..61af85c2fe 100644 --- a/packages/query-persist-client-core/src/persist.ts +++ b/packages/query-persist-client-core/src/persist.ts @@ -83,12 +83,10 @@ export async function persistQueryClientRestore({ } } catch (err) { if (process.env.NODE_ENV !== 'production') { - queryClient.getLogger().error(err) - queryClient - .getLogger() - .warn( - 'Encountered an error attempting to restore client cache from persisted location. As a precaution, the persisted cache will be discarded.', - ) + console.error(err) + console.warn( + 'Encountered an error attempting to restore client cache from persisted location. As a precaution, the persisted cache will be discarded.', + ) } persister.removeClient() } diff --git a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx index 7522b8872a..68efc35a16 100644 --- a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx +++ b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx @@ -12,7 +12,7 @@ import type { } from '@tanstack/query-persist-client-core' import { persistQueryClientSave } from '@tanstack/query-persist-client-core' -import { createQueryClient, mockLogger, queryKey, sleep } from './utils' +import { createQueryClient, queryKey, sleep } from './utils' import { PersistQueryClientProvider } from '../PersistQueryClientProvider' const createMockPersister = (): Persister => { @@ -402,8 +402,11 @@ describe('PersistQueryClientProvider', () => { test('should remove cache after non-successful restoring', async () => { const key = queryKey() - jest.spyOn(console, 'warn').mockImplementation(() => undefined) - jest.spyOn(console, 'error').mockImplementation(() => undefined) + const consoleMock = jest.spyOn(console, 'error') + const consoleWarn = jest + .spyOn(console, 'warn') + .mockImplementation(() => undefined) + consoleMock.mockImplementation(() => undefined) const queryClient = createQueryClient() const removeClient = jest.fn() @@ -438,8 +441,10 @@ describe('PersistQueryClientProvider', () => { await waitFor(() => rendered.getByText('fetched')) expect(removeClient).toHaveBeenCalledTimes(1) - expect(mockLogger.error).toHaveBeenCalledTimes(2) - expect(mockLogger.error).toHaveBeenNthCalledWith(2, error) + expect(consoleMock).toHaveBeenCalledTimes(1) + expect(consoleMock).toHaveBeenNthCalledWith(1, error) + consoleMock.mockRestore() + consoleWarn.mockRestore() }) test('should be able to persist into multiple clients', async () => { diff --git a/packages/react-query-persist-client/src/__tests__/utils.ts b/packages/react-query-persist-client/src/__tests__/utils.ts index 312280698a..8667303dc6 100644 --- a/packages/react-query-persist-client/src/__tests__/utils.ts +++ b/packages/react-query-persist-client/src/__tests__/utils.ts @@ -4,14 +4,7 @@ import type { QueryClientConfig } from '@tanstack/query-core' import { QueryClient } from '@tanstack/query-core' export function createQueryClient(config?: QueryClientConfig): QueryClient { - jest.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) -} - -export const mockLogger = { - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), + return new QueryClient(config) } let queryKeyCount = 0 diff --git a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx index 12633a0ab1..c07252f4e3 100644 --- a/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx +++ b/packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx @@ -17,6 +17,9 @@ describe('QueryErrorResetBoundary', () => { const queryClient = createQueryClient({ queryCache }) it('should retry fetch if the reset error boundary has been reset', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = false @@ -68,9 +71,13 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) + consoleMock.mockRestore() }) it('should not throw error if query is disabled', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = false @@ -128,9 +135,14 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('status: error')) + consoleMock.mockRestore() }) it('should not throw error if query is disabled, and refetch if query becomes enabled again', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let succeed = false @@ -189,9 +201,14 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) + consoleMock.mockRestore() }) it('should throw error if query is disabled and manually refetched', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() function Page() { @@ -246,9 +263,14 @@ describe('QueryErrorResetBoundary', () => { ) fireEvent.click(rendered.getByRole('button', { name: /refetch/i })) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should not retry fetch if the reset error boundary has not been reset', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let succeed = false @@ -299,9 +321,14 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should retry fetch if the reset error boundary has been reset and the query contains data from a previous fetch', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let succeed = false @@ -354,9 +381,14 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) + consoleMock.mockRestore() }) it('should not retry fetch if the reset error boundary has not been reset after a previous reset', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let succeed = false @@ -417,9 +449,14 @@ describe('QueryErrorResetBoundary', () => { shouldReset = false fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should throw again on error after the reset error boundary has been reset', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let fetchCount = 0 @@ -470,9 +507,14 @@ describe('QueryErrorResetBoundary', () => { fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) expect(fetchCount).toBe(3) + consoleMock.mockRestore() }) it('should never render the component while the query is in error state', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let fetchCount = 0 let renders = 0 @@ -532,9 +574,14 @@ describe('QueryErrorResetBoundary', () => { await waitFor(() => rendered.getByText('data')) expect(fetchCount).toBe(3) expect(renders).toBe(1) + consoleMock.mockRestore() }) it('should render children', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + function Page() { return (
    @@ -551,9 +598,14 @@ describe('QueryErrorResetBoundary', () => { ) expect(rendered.queryByText('page')).not.toBeNull() + consoleMock.mockRestore() }) it('should show error boundary when using tracked queries even though we do not track the error field', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() let succeed = false @@ -605,5 +657,6 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) + consoleMock.mockRestore() }) }) diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index aa372a0163..85e23524af 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -272,6 +272,9 @@ describe("useQuery's in Suspense mode", () => { // https://github.com/tannerlinsley/react-query/issues/468 it('should reset error state if new component instances are mounted', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = false @@ -332,9 +335,18 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) + + expect(consoleMock).toHaveBeenCalledWith( + expect.objectContaining(new Error('Suspense Error Bingo')), + ) + + consoleMock.mockRestore() }) it('should retry fetch if the reset error boundary has been reset', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = false @@ -392,6 +404,7 @@ describe("useQuery's in Suspense mode", () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) + consoleMock.mockRestore() }) it('should refetch when re-mounting', async () => { @@ -496,6 +509,9 @@ describe("useQuery's in Suspense mode", () => { }) it('should retry fetch if the reset error boundary has been reset with global hook', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = false @@ -553,9 +569,13 @@ describe("useQuery's in Suspense mode", () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) + consoleMock.mockRestore() }) it('should throw errors to the error boundary by default', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() function Page() { @@ -591,6 +611,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should not throw errors to the error boundary when throwErrors: false', async () => { @@ -633,6 +654,9 @@ describe("useQuery's in Suspense mode", () => { }) it('should throw errors to the error boundary when a throwErrors function returns true', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() function Page() { @@ -669,6 +693,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should not throw errors to the error boundary when a throwErrors function returns false', async () => { @@ -755,6 +780,9 @@ describe("useQuery's in Suspense mode", () => { }) it('should error catched in error boundary without infinite loop', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() let succeed = true @@ -817,9 +845,17 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByLabelText('fail')) // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) + expect(consoleMock).toHaveBeenCalledWith( + expect.objectContaining(new Error('Suspense Error Bingo')), + ) + + consoleMock.mockRestore() }) it('should error catched in error boundary without infinite loop when query keys changed', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) let succeed = true function Page() { @@ -876,9 +912,17 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByLabelText('fail')) // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) + expect(consoleMock).toHaveBeenCalledWith( + expect.objectContaining(new Error('Suspense Error Bingo')), + ) + + consoleMock.mockRestore() }) it('should error catched in error boundary without infinite loop when enabled changed', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) function Page() { const queryKeys = '1' const [enabled, setEnabled] = React.useState(false) @@ -936,6 +980,11 @@ describe("useQuery's in Suspense mode", () => { // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) + expect(consoleMock).toHaveBeenCalledWith( + expect.objectContaining(new Error('Suspense Error Bingo')), + ) + + consoleMock.mockRestore() }) it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index cb07a22040..4196aa4b95 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -688,6 +688,9 @@ describe('useMutation', () => { }) it('should be able to throw an error when throwErrors is set to true', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) function Page() { const { mutate } = useMutation({ mutationFn: () => { @@ -723,9 +726,18 @@ describe('useMutation', () => { await waitFor(() => { expect(queryByText('error')).not.toBeNull() }) + + expect(consoleMock).toHaveBeenCalledWith( + expect.objectContaining(new Error('Expected mock error. All is well!')), + ) + + consoleMock.mockRestore() }) it('should be able to throw an error when throwErrors is a function that returns true', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) let boundary = false function Page() { const { mutate, error } = useMutation({ @@ -772,6 +784,7 @@ describe('useMutation', () => { await waitFor(() => { expect(queryByText('error boundary')).not.toBeNull() }) + consoleMock.mockRestore() }) it('should pass meta to mutation', async () => { diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 9bd4b01dac..8e08a548bc 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -776,6 +776,9 @@ describe('useQueries', () => { }) it("should throw error if in one of queries' queryFn throws and throwErrors is in use", async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() @@ -834,9 +837,13 @@ describe('useQueries', () => { await waitFor(() => rendered.getByText('error boundary')) await waitFor(() => rendered.getByText('single query error')) + consoleMock.mockRestore() }) it("should throw error if in one of queries' queryFn throws and throwErrors function resolves to true", async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() @@ -897,6 +904,7 @@ describe('useQueries', () => { await waitFor(() => rendered.getByText('error boundary')) await waitFor(() => rendered.getByText('single query error')) + consoleMock.mockRestore() }) it('should use provided custom queryClient', async () => { diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 2338b0a5b6..0e65f721ea 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -5,7 +5,6 @@ import { Blink, createQueryClient, expectType, - mockLogger, mockNavigatorOnLine, mockVisibilityState, queryKey, @@ -1120,11 +1119,11 @@ describe('useQuery', () => { rendered.getByText('error') }) - expect(mockLogger.error).toHaveBeenCalledWith(error) expect(states.length).toBe(2) expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) - expect(states[1]).toMatchObject({ status: 'error', error }) + // expect(states[1]).toMatchObject({ status: 'error', error }) + expect(states[1]).toMatchObject({ status: 'error', error: error }) }) it('should not re-run a stable select when it re-renders if selector throws an error', async () => { @@ -2892,6 +2891,9 @@ describe('useQuery', () => { }) it('should throw error if queryFn throws and throwErrors is in use', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) const key = queryKey() function Page() { @@ -2918,6 +2920,7 @@ describe('useQuery', () => { ) await waitFor(() => rendered.getByText('error boundary')) + consoleMock.mockRestore() }) it('should update with data if we observe no properties and throwErrors', async () => { @@ -2980,6 +2983,10 @@ describe('useQuery', () => { }) it('should throw error instead of setting status when error should be thrown', async () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const key = queryKey() function Page() { @@ -3016,6 +3023,7 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error boundary')) await waitFor(() => rendered.getByText('Remote Error')) + consoleMock.mockRestore() }) it('should continue retries when observers unmount and remount while waiting for a retry (#3031)', async () => { @@ -3519,9 +3527,6 @@ describe('useQuery', () => { await sleep(10) await waitFor(() => rendered.getByText('failureCount 4')) await waitFor(() => rendered.getByText('failureReason fetching error 4')) - - // Check if the error has been logged in the console - expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4') }) it('should fetch on mount when a query was already created with setQueryData', async () => { diff --git a/packages/react-query/src/__tests__/utils.tsx b/packages/react-query/src/__tests__/utils.tsx index a45777f790..a0f66ca852 100644 --- a/packages/react-query/src/__tests__/utils.tsx +++ b/packages/react-query/src/__tests__/utils.tsx @@ -41,8 +41,7 @@ export const Blink = ({ } export function createQueryClient(config?: QueryClientConfig): QueryClient { - jest.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) + return new QueryClient(config) } export function mockVisibilityState(value: DocumentVisibilityState) { @@ -53,12 +52,6 @@ export function mockNavigatorOnLine(value: boolean) { return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } -export const mockLogger = { - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), -} - let queryKeyCount = 0 export function queryKey(): Array { queryKeyCount++ diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 2a2e39ae6c..b495dc50e4 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -27,7 +27,6 @@ import { Blink, createQueryClient, expectType, - mockLogger, mockNavigatorOnLine, mockVisibilityState, queryKey, @@ -1154,7 +1153,6 @@ describe('createQuery', () => { await sleep(10) - expect(mockLogger.error).toHaveBeenCalledWith(error) expect(states.length).toBe(2) expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) @@ -3481,9 +3479,6 @@ describe('createQuery', () => { await sleep(10) await waitFor(() => screen.getByText('failureCount 4')) await waitFor(() => screen.getByText('failureReason fetching error 4')) - - // Check if the error has been logged in the console - expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4') }) it('should fetch on mount when a query was already created with setQueryData', async () => { diff --git a/packages/solid-query/src/__tests__/utils.tsx b/packages/solid-query/src/__tests__/utils.tsx index 964d696593..8122f7ec0f 100644 --- a/packages/solid-query/src/__tests__/utils.tsx +++ b/packages/solid-query/src/__tests__/utils.tsx @@ -30,8 +30,7 @@ export const Blink = ( } export function createQueryClient(config?: QueryClientConfig): QueryClient { - jest.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) + return new QueryClient(config) } export function mockVisibilityState(value: DocumentVisibilityState) { @@ -42,12 +41,6 @@ export function mockNavigatorOnLine(value: boolean) { return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } -export const mockLogger = { - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), -} - export function sleep(timeout: number): Promise { return new Promise((resolve, _reject) => { setTimeout(resolve, timeout) diff --git a/packages/svelte-query/src/__tests__/utils.ts b/packages/svelte-query/src/__tests__/utils.ts index 2ccf931daf..be73653e86 100644 --- a/packages/svelte-query/src/__tests__/utils.ts +++ b/packages/svelte-query/src/__tests__/utils.ts @@ -3,8 +3,7 @@ import { act } from '@testing-library/svelte' import { QueryClient, type QueryClientConfig } from '../index' export function createQueryClient(config?: QueryClientConfig): QueryClient { - vi.spyOn(console, 'error').mockImplementation(() => undefined) - return new QueryClient({ logger: mockLogger, ...config }) + return new QueryClient(config) } export function mockVisibilityState(value: DocumentVisibilityState) { diff --git a/packages/vue-query/src/__mocks__/useQueryClient.ts b/packages/vue-query/src/__mocks__/useQueryClient.ts index 65645ef45c..ae057aa119 100644 --- a/packages/vue-query/src/__mocks__/useQueryClient.ts +++ b/packages/vue-query/src/__mocks__/useQueryClient.ts @@ -1,12 +1,6 @@ import { QueryClient } from '../queryClient' const queryClient = new QueryClient({ - logger: { - ...console, - error: () => { - // Noop - }, - }, defaultOptions: { queries: { retry: false, gcTime: Infinity }, }, diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 3e31f23899..144d1d4252 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -32,7 +32,6 @@ export class QueryClient extends QC { constructor(config: MaybeRefDeep = {}) { const unreffedConfig = cloneDeepUnref(config) const vueQueryConfig: QueryClientConfig = { - logger: unreffedConfig.logger, defaultOptions: unreffedConfig.defaultOptions, queryCache: unreffedConfig.queryCache || new QueryCache(), mutationCache: unreffedConfig.mutationCache || new MutationCache(), diff --git a/rollup.config.ts b/rollup.config.ts index edd8cce6fb..680087bd79 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -40,7 +40,7 @@ export default function rollup(options: RollupOptions): RollupOptions[] { packageDir: 'packages/query-core', jsName: 'QueryCore', outputFile: 'index', - entryFile: ['src/index.ts', 'src/logger.native.ts'], + entryFile: ['src/index.ts'], globals: {}, }), ...buildConfigs({ @@ -89,18 +89,14 @@ export default function rollup(options: RollupOptions): RollupOptions[] { packageDir: 'packages/react-query', jsName: 'ReactQuery', outputFile: 'index', - entryFile: [ - 'src/index.ts', - ], + entryFile: ['src/index.ts'], globals: { react: 'React', 'react-dom': 'ReactDOM', '@tanstack/query-core': 'QueryCore', 'react-native': 'ReactNative', }, - bundleUMDGlobals: [ - '@tanstack/query-core', - ], + bundleUMDGlobals: ['@tanstack/query-core'], }), ...buildConfigs({ name: 'react-query-devtools', @@ -115,10 +111,7 @@ export default function rollup(options: RollupOptions): RollupOptions[] { '@tanstack/match-sorter-utils': 'MatchSorterUtils', superjson: 'SuperJson', }, - bundleUMDGlobals: [ - '@tanstack/match-sorter-utils', - 'superjson', - ], + bundleUMDGlobals: ['@tanstack/match-sorter-utils', 'superjson'], }), ...buildConfigs({ name: 'react-query-devtools-prod', @@ -356,12 +349,6 @@ function cjs({ commonJS(), nodeResolve({ extensions: ['.ts', '.tsx', '.native.ts'] }), forceDevEnv ? forceEnvPlugin('development') : undefined, - replace({ - // TODO: figure out a better way to produce extensionless cjs imports - "require('./logger.js')": "require('./logger')", - preventAssignment: true, - delimiters: ['', ''], - }), ], } } From 5f768595b412014a4495b911023f1765d6e8bc02 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 25 Jan 2023 20:01:44 +0100 Subject: [PATCH 027/314] docs: remove custom logger --- docs/config.json | 8 ------ docs/react/guides/custom-logger.md | 27 -------------------- docs/react/guides/migrating-to-v5.md | 6 ++++- docs/react/guides/testing.md | 18 ------------- docs/react/reference/QueryClient.md | 11 -------- docs/vue/guides/custom-logger.md | 5 ---- packages/svelte-query/src/__tests__/utils.ts | 6 ----- 7 files changed, 5 insertions(+), 76 deletions(-) delete mode 100644 docs/react/guides/custom-logger.md delete mode 100644 docs/vue/guides/custom-logger.md diff --git a/docs/config.json b/docs/config.json index b8ec9290e2..d1f265f91a 100644 --- a/docs/config.json +++ b/docs/config.json @@ -164,10 +164,6 @@ "label": "Suspense", "to": "react/guides/suspense" }, - { - "label": "Custom Logger", - "to": "react/guides/custom-logger" - }, { "label": "Testing", "to": "react/guides/testing" @@ -562,10 +558,6 @@ "label": "Suspense", "to": "vue/guides/suspense" }, - { - "label": "Custom Logger", - "to": "vue/guides/custom-logger" - }, { "label": "Testing", "to": "vue/guides/testing" diff --git a/docs/react/guides/custom-logger.md b/docs/react/guides/custom-logger.md deleted file mode 100644 index 6213d3f18f..0000000000 --- a/docs/react/guides/custom-logger.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -id: custom-logger -title: Custom Logger ---- - -If you want to change how information is logged by TanStack Query, you can set a custom logger when creating a `QueryClient`. - -```tsx -const queryClient = new QueryClient({ - logger: { - log: (...args) => { - // Log debugging information - }, - warn: (...args) => { - // Log warning - }, - error: (...args) => { - // Log error - }, - }, -}) -``` - -**Deprecated** - -Custom loggers have been deprecated and will be removed in the next major version. -Logging only has an effect in development mode, where passing a custom logger is not necessary. diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 22dd2514c9..c7a5badc77 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -117,7 +117,7 @@ if you still need to remove a query, you can use `queryClient.removeQueries({que Mainly because an important fix was shipped around type inference. Please see this [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371) for more information. -### The `isDataEqual` options has been removed from useQuery +### The `isDataEqual` option has been removed from useQuery Previously, This function was used to indicate whether to use previous `data` (`true`) or new data (`false`) as a resolved data for the query. @@ -130,6 +130,10 @@ You can achieve the same functionality by passing a function to `structuralShari + structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) ``` +### The deprecated custom logger has been removed + +Custom loggers were already deprecated in 4 and have been removed in this version. Logging only had an effect in development mode, where passing a custom logger is not necessary. + ### Supported Browsers We have updated our browserslist to produce a more modern, performant and smaller bundle. You can read about the requirements [here](../installation#requirements). diff --git a/docs/react/guides/testing.md b/docs/react/guides/testing.md index 350e526d7b..6d0dfa6d21 100644 --- a/docs/react/guides/testing.md +++ b/docs/react/guides/testing.md @@ -82,24 +82,6 @@ const wrapper = ({ children }) => ( This will set the defaults for all queries in the component tree to "no retries". It is important to know that this will only work if your actual useQuery has no explicit retries set. If you have a query that wants 5 retries, this will still take precedence, because defaults are only taken as a fallback. -## Turn off network error logging - -When testing we want to suppress network errors being logged to the console. -To do this, we can pass a custom logger to `QueryClient`: - -```tsx -import { QueryClient } from '@tanstack/react-query' - -const queryClient = new QueryClient({ - logger: { - log: console.log, - warn: console.warn, - // ✅ no more errors on the console for tests - error: process.env.NODE_ENV === 'test' ? () => {} : console.error, - }, -}) -``` - ## Set cacheTime to Infinity with Jest If you use Jest, you can set the `cacheTime` to `Infinity` to prevent "Jest did not exit one second after the test run completed" error message. This is the default behavior on the server, and is only necessary to set if you are explicitly setting a `cacheTime`. diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index 41a1bd4cf9..a380129688 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -40,7 +40,6 @@ Its available methods are: - [`queryClient.resetQueries`](#queryclientresetqueries) - [`queryClient.isFetching`](#queryclientisfetching) - [`queryClient.isMutating`](#queryclientismutating) -- [`queryClient.getLogger`](#queryclientgetlogger) - [`queryClient.getDefaultOptions`](#queryclientgetdefaultoptions) - [`queryClient.setDefaultOptions`](#queryclientsetdefaultoptions) - [`queryClient.getQueryDefaults`](#queryclientgetquerydefaults) @@ -60,9 +59,6 @@ Its available methods are: - `mutationCache?: MutationCache` - Optional - The mutation cache this client is connected to. -- `logger?: Logger` - - Optional - - The logger this client uses to log debugging information, warnings and errors. If not set, `console` is the default logger. - `defaultOptions?: DefaultOptions` - Optional - Define defaults for all queries and mutations using this queryClient. @@ -466,13 +462,6 @@ TanStack Query also exports a handy [`useIsMutating`](../reference/useIsMutating **Returns** This method returns the number of fetching mutations. -## `queryClient.getLogger` - -The `getLogger` method returns the logger which have been set when creating the client. - -```tsx -const logger = queryClient.getLogger() -``` ## `queryClient.getDefaultOptions` diff --git a/docs/vue/guides/custom-logger.md b/docs/vue/guides/custom-logger.md deleted file mode 100644 index fe6d7ce393..0000000000 --- a/docs/vue/guides/custom-logger.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -id: custom-logger -title: Custom Logger -ref: docs/react/guides/custom-logger.md ---- diff --git a/packages/svelte-query/src/__tests__/utils.ts b/packages/svelte-query/src/__tests__/utils.ts index be73653e86..67488053f2 100644 --- a/packages/svelte-query/src/__tests__/utils.ts +++ b/packages/svelte-query/src/__tests__/utils.ts @@ -14,12 +14,6 @@ export function mockNavigatorOnLine(value: boolean) { return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } -export const mockLogger = { - log: vi.fn(), - warn: vi.fn(), - error: vi.fn(), -} - let queryKeyCount = 0 export function queryKey(): Array { queryKeyCount++ From d8516b4c71e236bb471ab74cc7f04c75bf5b65f7 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Thu, 26 Jan 2023 14:00:05 +0100 Subject: [PATCH 028/314] test: add fail-on-console plugin (#4880) * test: add fail-on-console plugin * chore: revert lock file changes and use pnpm add instead --- jest-preset.js | 1 + jest.setup.js | 4 ++++ package.json | 1 + pnpm-lock.yaml | 8 ++++++++ 4 files changed, 14 insertions(+) create mode 100644 jest.setup.js diff --git a/jest-preset.js b/jest-preset.js index 09003bfb75..6d1afc8705 100644 --- a/jest-preset.js +++ b/jest-preset.js @@ -26,6 +26,7 @@ module.exports = { coverageReporters: ['json', 'lcov', 'text', 'clover', 'text-summary'], testMatch: ['/**/src/**/*.test.[jt]s?(x)'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest' }, + setupFilesAfterEnv: ['../../jest.setup.js'], clearMocks: true, testEnvironment: 'jsdom', snapshotFormat: { diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000000..1424b4e5c3 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const failOnConsole = require('jest-fail-on-console') + +failOnConsole() diff --git a/package.json b/package.json index abed8abd25..5add275801 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "eslint-plugin-standard": "^4.0.1", "git-log-parser": "^1.2.0", "jest": "^27.5.1", + "jest-fail-on-console": "^3.0.2", "jsonfile": "^6.1.0", "luxon": "^2.3.2", "prettier": "^2.6.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cafcc2a34..cb27c848b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,7 @@ importers: eslint-plugin-standard: ^4.0.1 git-log-parser: ^1.2.0 jest: ^27.5.1 + jest-fail-on-console: ^3.0.2 jsonfile: ^6.1.0 luxon: ^2.3.2 prettier: ^2.6.2 @@ -115,6 +116,7 @@ importers: eslint-plugin-standard: 4.1.0_eslint@7.32.0 git-log-parser: 1.2.0 jest: 27.5.1_ts-node@10.8.2 + jest-fail-on-console: 3.0.2 jsonfile: 6.1.0 luxon: 2.4.0 prettier: 2.7.1 @@ -11837,6 +11839,12 @@ packages: jest-util: 27.5.1 dev: true + /jest-fail-on-console/3.0.2: + resolution: {integrity: sha512-8vpH03d9n41jKCF/rcQz/nf0ksK2hlnOXTY5VUMqliPHPfT9F7L9CYOyhFNSQ+o6Aszsuja0GAXt1jz6sfDM8w==} + dependencies: + chalk: 4.1.2 + dev: true + /jest-get-type/26.3.0: resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==} engines: {node: '>= 10.14.2'} From edebd87abc266faff1174fe6bead2d578c6ad6b4 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Mon, 30 Jan 2023 12:08:30 +0100 Subject: [PATCH 029/314] refactor: don't mutate incoming filter objects --- packages/query-core/src/queryClient.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index c60ba5033a..43167ca5e6 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -97,9 +97,9 @@ export class QueryClient { this.#unsubscribeOnline = undefined } - isFetching(filters: QueryFilters = {}): number { - filters.fetchStatus = 'fetching' - return this.#queryCache.findAll(filters).length + isFetching(filters?: QueryFilters): number { + return this.#queryCache.findAll({ ...filters, fetchStatus: 'fetching' }) + .length } isMutating(filters?: MutationFilters): number { From 91b165675e7a913fc9faab0bcc176aaef8c59a63 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Mon, 30 Jan 2023 16:03:53 +0100 Subject: [PATCH 030/314] fix: cacheTime -> gcTime --- packages/query-core/src/tests/query.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 8288882929..af5845254c 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -818,7 +818,7 @@ describe('query', () => { test('queries should be garbage collected even if they never fetched', async () => { const key = queryKey() - queryClient.setQueryDefaults(key, { cacheTime: 10 }) + queryClient.setQueryDefaults(key, { gcTime: 10 }) const fn = jest.fn() From 50b6a81f069c514a63cb6abda9c772f4f546465e Mon Sep 17 00:00:00 2001 From: Damian Osipiuk Date: Sun, 5 Feb 2023 19:16:49 +0100 Subject: [PATCH 031/314] test: fix svelte-query test (#4937) --- .../src/__tests__/CreateQueries.svelte | 2 +- .../src/__tests__/CreateQuery.svelte | 2 +- .../src/__tests__/createQueries.test.ts | 30 ++++++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/svelte-query/src/__tests__/CreateQueries.svelte b/packages/svelte-query/src/__tests__/CreateQueries.svelte index 5a64918919..d8908f2b50 100644 --- a/packages/svelte-query/src/__tests__/CreateQueries.svelte +++ b/packages/svelte-query/src/__tests__/CreateQueries.svelte @@ -3,7 +3,7 @@ import { setQueryClientContext } from '../context' import type { QueriesOptions } from '../createQueries' - export let options: readonly [...QueriesOptions] + export let options: { queries: readonly [...QueriesOptions] } const queryClient = new QueryClient() setQueryClientContext(queryClient) diff --git a/packages/svelte-query/src/__tests__/CreateQuery.svelte b/packages/svelte-query/src/__tests__/CreateQuery.svelte index d4d5e6d45f..9b61fab993 100644 --- a/packages/svelte-query/src/__tests__/CreateQuery.svelte +++ b/packages/svelte-query/src/__tests__/CreateQuery.svelte @@ -2,7 +2,7 @@ import { createQuery, QueryClient, type CreateQueryOptions } from '../index' import { setQueryClientContext } from '../context' - export let options: CreateQueryOptions + export let options: CreateQueryOptions const queryClient = new QueryClient() setQueryClientContext(queryClient) diff --git a/packages/svelte-query/src/__tests__/createQueries.test.ts b/packages/svelte-query/src/__tests__/createQueries.test.ts index f751209d46..9463106bb1 100644 --- a/packages/svelte-query/src/__tests__/createQueries.test.ts +++ b/packages/svelte-query/src/__tests__/createQueries.test.ts @@ -7,22 +7,24 @@ describe('createQueries', () => { it('Render and wait for success', async () => { render(CreateQueries, { props: { - options: [ - { - queryKey: ['key-1'], - queryFn: async () => { - await sleep(10) - return 'Success 1' + options: { + queries: [ + { + queryKey: ['key-1'], + queryFn: async () => { + await sleep(10) + return 'Success 1' + }, }, - }, - { - queryKey: ['key-2'], - queryFn: async () => { - await sleep(10) - return 'Success 2' + { + queryKey: ['key-2'], + queryFn: async () => { + await sleep(10) + return 'Success 2' + }, }, - }, - ], + ], + }, }, }) From b66f92aafb1591b1a4fb5a9edddb795992cb68c0 Mon Sep 17 00:00:00 2001 From: Damian Osipiuk Date: Sun, 5 Feb 2023 19:50:56 +0100 Subject: [PATCH 032/314] fix(vue-query): useQueries length change detection fix for vue2 (#4934) * fix(vue-query): useQueries length change detection fix for vue2 BREAKING CHANGE: useQueries returns `ref` instead of `reactive` --- docs/config.json | 4 + docs/vue/guides/migrating-to-v5.md | 24 + packages/vue-query/__mocks__/vue-demi.ts | 14 +- packages/vue-query/package.json | 14 +- .../src/__tests__/queryCache.test.ts | 10 +- .../src/__tests__/useQueries.test.ts | 20 +- .../src/__tests__/vueQueryPlugin.test.ts | 12 +- packages/vue-query/src/useQueries.ts | 24 +- pnpm-lock.yaml | 529 ++++++++++++------ 9 files changed, 442 insertions(+), 209 deletions(-) create mode 100644 docs/vue/guides/migrating-to-v5.md diff --git a/docs/config.json b/docs/config.json index d1f265f91a..f7ed6c171e 100644 --- a/docs/config.json +++ b/docs/config.json @@ -565,6 +565,10 @@ { "label": "Does this replace [Vuex, Pinia]?", "to": "vue/guides/does-this-replace-client-state" + }, + { + "label": "Migrating to v5", + "to": "react/guides/migrating-to-v5" } ] }, diff --git a/docs/vue/guides/migrating-to-v5.md b/docs/vue/guides/migrating-to-v5.md new file mode 100644 index 0000000000..a022fbc1ca --- /dev/null +++ b/docs/vue/guides/migrating-to-v5.md @@ -0,0 +1,24 @@ +--- +id: migrating-to-tanstack-query-5 +title: Migrating to TanStack Query v5 +ref: docs/react/guides/migrating-to-v5.md +--- + +[//]: # 'FrameworkBreakingChanges' + +## Vue Query Breaking Changes + +### `useQueries` composable returns `ref` instead of `reactive` + +To fix compatibility with Vue 2, `useQueries` hook returns now `queries` array wrapped in `ref`. +Previously `reactive` was returned which led to multiple problems: + +- User could spread return value loosing reactivity. +- `readonly` wrapper used for return value was breaking Vue 2 reactivity detection mechanism. This was a silent issue in Vue 2.6, but appeared as error in Vue 2.7. +- Vue 2 does not support arrays as a root value of `reactive`. + +With this change all of those issues are fixed. + +Also this aligns `useQueries` with other composables which return all of the values as `refs`. + +[//]: # 'FrameworkBreakingChanges' diff --git a/packages/vue-query/__mocks__/vue-demi.ts b/packages/vue-query/__mocks__/vue-demi.ts index dde832fe38..3525ecb570 100644 --- a/packages/vue-query/__mocks__/vue-demi.ts +++ b/packages/vue-query/__mocks__/vue-demi.ts @@ -1,4 +1,14 @@ -const vue = jest.requireActual("vue-demi"); +// Hide annoying console warnings for Vue2 +import Vue from 'vue2' +Vue.config.productionTip = false +Vue.config.devtools = false + +// Hide annoying console warnings for Vue2 +import Vue27 from 'vue2.7' +Vue27.config.productionTip = false +Vue27.config.devtools = false + +const vue = jest.requireActual('vue-demi') module.exports = { ...vue, @@ -6,4 +16,4 @@ module.exports = { provide: jest.fn(), onScopeDispose: jest.fn(), getCurrentInstance: jest.fn(() => ({ proxy: {} })), -}; +} diff --git a/packages/vue-query/package.json b/packages/vue-query/package.json index ba91519360..4419c360b4 100644 --- a/packages/vue-query/package.json +++ b/packages/vue-query/package.json @@ -30,7 +30,10 @@ "clean": "rimraf ./build", "test:eslint": "eslint --ext .ts ./src", "test:types": "tsc", - "test:lib": "jest --config ./jest.config.ts", + "test:lib": "pnpm run test:2 && pnpm run test:2.7 && pnpm run test:3", + "test:2": "vue-demi-switch 2 vue2 && jest --config ./jest.config.ts", + "test:2.7": "vue-demi-switch 2.7 vue2.7 && jest --config ./jest.config.ts", + "test:3": "vue-demi-switch 3 && jest --config ./jest.config.ts", "test:lib:dev": "pnpm run test:lib --watch", "build:types": "tsc --build" }, @@ -40,13 +43,14 @@ ], "devDependencies": { "@vue/composition-api": "1.7.1", - "vue": "^3.2.40", - "vue2": "npm:vue@2" + "vue": "^3.2.47", + "vue2": "npm:vue@2.6", + "vue2.7": "npm:vue@2.7" }, "dependencies": { "@tanstack/query-core": "workspace:*", - "@vue/devtools-api": "^6.4.2", - "@tanstack/match-sorter-utils": "^8.1.1", + "@vue/devtools-api": "^6.5.0", + "@tanstack/match-sorter-utils": "^8.7.6", "vue-demi": "^0.13.11" }, "peerDependencies": { diff --git a/packages/vue-query/src/__tests__/queryCache.test.ts b/packages/vue-query/src/__tests__/queryCache.test.ts index ee7bc9c63a..de0d85690d 100644 --- a/packages/vue-query/src/__tests__/queryCache.test.ts +++ b/packages/vue-query/src/__tests__/queryCache.test.ts @@ -37,16 +37,12 @@ describe('QueryCache', () => { }) }) - test('should properly unwrap one parameter', async () => { + test('should default to empty filters', async () => { const queryCache = new QueryCache() - queryCache.findAll({ - queryKey: ref(['baz']), - }) + queryCache.findAll() - expect(QueryCacheOrigin.prototype.findAll).toBeCalledWith({ - queryKey: ['baz'], - }) + expect(QueryCacheOrigin.prototype.findAll).toBeCalledWith({}) }) }) }) diff --git a/packages/vue-query/src/__tests__/useQueries.test.ts b/packages/vue-query/src/__tests__/useQueries.test.ts index 14ae31308c..a690a1e106 100644 --- a/packages/vue-query/src/__tests__/useQueries.test.ts +++ b/packages/vue-query/src/__tests__/useQueries.test.ts @@ -1,4 +1,4 @@ -import { onScopeDispose, reactive } from 'vue-demi' +import { onScopeDispose, ref } from 'vue-demi' import { flushPromises, @@ -26,7 +26,7 @@ describe('useQueries', () => { ] const queriesState = useQueries({ queries }) - expect(queriesState).toMatchObject([ + expect(queriesState.value).toMatchObject([ { status: 'loading', isLoading: true, @@ -57,7 +57,7 @@ describe('useQueries', () => { await flushPromises() - expect(queriesState).toMatchObject([ + expect(queriesState.value).toMatchObject([ { status: 'success', isLoading: false, @@ -88,7 +88,7 @@ describe('useQueries', () => { await flushPromises() - expect(queriesState).toMatchObject([ + expect(queriesState.value).toMatchObject([ { status: 'error', isLoading: false, @@ -105,7 +105,7 @@ describe('useQueries', () => { }) test('should return state for new queries', async () => { - const queries = reactive([ + const queries = ref([ { queryKey: ['key31'], queryFn: getSimpleFetcherWithReturnData('value31'), @@ -123,9 +123,9 @@ describe('useQueries', () => { await flushPromises() - queries.splice( + queries.value.splice( 0, - queries.length, + queries.value.length, { queryKey: ['key31'], queryFn: getSimpleFetcherWithReturnData('value31'), @@ -139,8 +139,8 @@ describe('useQueries', () => { await flushPromises() await flushPromises() - expect(queriesState.length).toEqual(2) - expect(queriesState).toMatchObject([ + expect(queriesState.value.length).toEqual(2) + expect(queriesState.value).toMatchObject([ { data: 'value31', status: 'success', @@ -177,7 +177,7 @@ describe('useQueries', () => { const queriesState = useQueries({ queries }) await flushPromises() - expect(queriesState).toMatchObject([ + expect(queriesState.value).toMatchObject([ { status: 'loading', isLoading: true, diff --git a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts index 8b74e444d0..488d596245 100644 --- a/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts +++ b/packages/vue-query/src/__tests__/vueQueryPlugin.test.ts @@ -197,10 +197,10 @@ describe('VueQueryPlugin', () => { }) appMock._mixin.beforeCreate?.call(appMock) + const client = appMock._provided.VUE_QUERY_CLIENT as QueryClient + const defaultOptions = client.getDefaultOptions() - expect(appMock._provided).toMatchObject({ - VUE_QUERY_CLIENT: expect.objectContaining(config), - }) + expect(defaultOptions).toEqual(config.defaultOptions) }, ) @@ -333,14 +333,14 @@ describe('VueQueryPlugin', () => { }) expect(customClient.isRestoring.value).toBeTruthy() - expect(queries[0].isFetching).toBeFalsy() - expect(queries[0].data).toStrictEqual(undefined) + expect(queries.value[0].isFetching).toBeFalsy() + expect(queries.value[0].data).toStrictEqual(undefined) expect(fnSpy).toHaveBeenCalledTimes(0) await flushPromises() expect(customClient.isRestoring.value).toBeFalsy() - expect(queries[0].data).toStrictEqual({ foo: 'bar' }) + expect(queries.value[0].data).toStrictEqual({ foo: 'bar' }) expect(fnSpy).toHaveBeenCalledTimes(0) }) }) diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index d2c754c721..b7e27df27c 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -4,14 +4,8 @@ import type { QueriesPlaceholderDataFunction, QueryKey, } from '@tanstack/query-core' -import { - computed, - onScopeDispose, - reactive, - readonly, - ref, - watch, -} from 'vue-demi' +import type { Ref } from 'vue-demi' +import { computed, onScopeDispose, readonly, ref, watch } from 'vue-demi' import type { QueryFunction, QueryObserverResult } from '@tanstack/query-core' @@ -157,7 +151,7 @@ export function useQueries({ }: { queries: MaybeRefDeep> queryClient?: QueryClient -}): Readonly> { +}): Readonly>> { const client = queryClient || useQueryClient() const defaultedQueries = computed(() => @@ -172,7 +166,7 @@ export function useQueries({ ) const observer = new QueriesObserver(client, defaultedQueries.value) - const state = reactive(observer.getCurrentResult()) + const state = ref(observer.getCurrentResult()) const unsubscribe = ref(() => { // noop @@ -184,12 +178,12 @@ export function useQueries({ if (!isRestoring) { unsubscribe.value() unsubscribe.value = observer.subscribe((result) => { - state.splice(0, result.length, ...result) + state.value.splice(0, result.length, ...result) }) // Subscription would not fire for persisted results - state.splice( + state.value.splice( 0, - state.length, + state.value.length, ...observer.getOptimisticResult(defaultedQueries.value), ) } @@ -201,7 +195,7 @@ export function useQueries({ defaultedQueries, () => { observer.setQueries(defaultedQueries.value) - state.splice(0, state.length, ...observer.getCurrentResult()) + state.value.splice(0, state.value.length, ...observer.getCurrentResult()) }, { deep: true }, ) @@ -210,5 +204,5 @@ export function useQueries({ unsubscribe.value() }) - return readonly(state) as UseQueriesResults + return readonly(state) as Readonly>> } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0e84d9d89..3958517431 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,8 +146,8 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 isomorphic-unfetch: 3.0.0 next: 12.2.2_biqbaboplfbrettd7655fr4n2y @@ -165,13 +165,13 @@ importers: react-dom: ^18.0.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: - '@tanstack/eslint-plugin-query': link:../../../packages/eslint-plugin-query + '@tanstack/eslint-plugin-query': 4.24.5 '@vitejs/plugin-react': 2.1.0_vite@3.1.3 vite: 3.1.3 @@ -186,8 +186,8 @@ importers: react-dom: ^18.0.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa graphql: 15.8.0 graphql-request: 3.7.0_graphql@15.8.0 react: 18.2.0 @@ -214,15 +214,15 @@ importers: typescript: 4.7.4 vite: ^3.0.0 dependencies: - '@tanstack/query-sync-storage-persister': link:../../../packages/query-sync-storage-persister - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools - '@tanstack/react-query-persist-client': link:../../../packages/react-query-persist-client + '@tanstack/query-sync-storage-persister': 4.24.4 + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa + '@tanstack/react-query-persist-client': 4.24.4_pbzrecrhgj7fe7hddoo6lp43hu axios: 0.26.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: - '@tanstack/eslint-plugin-query': link:../../../packages/eslint-plugin-query + '@tanstack/eslint-plugin-query': 4.24.5 '@types/react': 17.0.50 '@types/react-dom': 17.0.17 '@vitejs/plugin-react': 2.1.0_vite@3.1.4 @@ -241,8 +241,8 @@ importers: react-dom: ^18.0.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.26.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -261,8 +261,8 @@ importers: react-dom: ^18.2.0 react-intersection-observer: ^8.33.1 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 isomorphic-unfetch: 3.0.0 next: 12.2.2_biqbaboplfbrettd7655fr4n2y @@ -282,8 +282,8 @@ importers: resolve-from: ^5.0.0 web-streams-polyfill: ^3.0.3 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa ky: 0.23.0 ky-universal: 0.8.2_53xdiffegfcxt6522645rot5ue next: 12.2.2_biqbaboplfbrettd7655fr4n2y @@ -307,11 +307,11 @@ importers: react-hot-toast: ^2.2.0 vite: ^3.0.0 dependencies: - '@tanstack/query-sync-storage-persister': link:../../../packages/query-sync-storage-persister + '@tanstack/query-sync-storage-persister': 4.24.4 '@tanstack/react-location': 3.7.4_biqbaboplfbrettd7655fr4n2y - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools - '@tanstack/react-query-persist-client': link:../../../packages/react-query-persist-client + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa + '@tanstack/react-query-persist-client': 4.24.4_pbzrecrhgj7fe7hddoo6lp43hu ky: 0.30.0 msw: 0.39.2 react: 18.2.0 @@ -334,8 +334,8 @@ importers: react-dom: ^18.2.0 typescript: 4.7.4 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa '@types/node': 14.14.14 '@types/react': 18.0.15 axios: 0.21.4 @@ -355,8 +355,8 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 isomorphic-unfetch: 3.0.0 next: 12.2.2_biqbaboplfbrettd7655fr4n2y @@ -372,8 +372,8 @@ importers: react-dom: ^18.0.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: @@ -390,8 +390,8 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 isomorphic-unfetch: 3.0.0 next: 12.2.2_biqbaboplfbrettd7655fr4n2y @@ -431,8 +431,8 @@ importers: '@react-native-community/netinfo': 6.0.2_react-native@0.64.3 '@react-navigation/native': 6.0.11_sbjh7r6wrxe2pvsvaqturwwxna '@react-navigation/stack': 6.2.2_dpltcvsy22isyfoj2zvicex7ry - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_6xi2cjj4jkvnn2q2msxp65enam + '@tanstack/react-query-devtools': 4.24.4_bqexmznvdh3tavatamdji3xwki expo: 43.0.5_@babel+core@7.19.1 expo-constants: 12.1.3 expo-status-bar: 1.1.0 @@ -471,8 +471,8 @@ importers: sort-by: ^1.2.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa localforage: 1.10.0 match-sorter: 6.3.1 react: 18.2.0 @@ -497,8 +497,8 @@ importers: vite: ^3.0.0 dependencies: '@material-ui/core': 4.12.4_biqbaboplfbrettd7655fr4n2y - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-router: 5.3.3_react@18.2.0 @@ -517,8 +517,8 @@ importers: react-dom: ^18.0.0 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.26.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -539,8 +539,8 @@ importers: vite: ^3.0.0 dependencies: '@material-ui/core': 4.12.4_biqbaboplfbrettd7655fr4n2y - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-router: 5.3.3_react@18.2.0 @@ -560,8 +560,8 @@ importers: react-error-boundary: ^2.2.3 vite: ^3.0.0 dependencies: - '@tanstack/react-query': link:../../../packages/react-query - '@tanstack/react-query-devtools': link:../../../packages/react-query-devtools + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + '@tanstack/react-query-devtools': 4.24.4_pkeil6ml7pq7xvil3imldjs2sa axios: 0.21.4 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -580,7 +580,7 @@ importers: vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: - '@tanstack/solid-query': link:../../../packages/solid-query + '@tanstack/solid-query': 4.24.4_solid-js@1.5.4 graphql: 16.6.0 graphql-request: 5.0.0_graphql@16.6.0 solid-js: 1.5.4 @@ -597,7 +597,7 @@ importers: vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: - '@tanstack/solid-query': link:../../../packages/solid-query + '@tanstack/solid-query': 4.24.4_solid-js@1.5.4 solid-js: 1.5.4 devDependencies: typescript: 4.7.4 @@ -612,7 +612,7 @@ importers: vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: - '@tanstack/solid-query': link:../../../packages/solid-query + '@tanstack/solid-query': 4.24.4_solid-js@1.5.4 solid-js: 1.5.4 devDependencies: typescript: 4.7.4 @@ -628,10 +628,10 @@ importers: vite: ^3.0.9 vite-plugin-solid: ^2.3.9 dependencies: - '@tanstack/solid-query': link:../../../packages/solid-query + '@tanstack/solid-query': 4.24.4_solid-js@1.5.4 solid-js: 1.5.4 devDependencies: - '@tanstack/eslint-plugin-query': link:../../../packages/eslint-plugin-query + '@tanstack/eslint-plugin-query': 4.24.5 typescript: 4.7.4 vite: 3.1.3 vite-plugin-solid: 2.3.9_solid-js@1.5.4+vite@3.1.3 @@ -647,7 +647,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -668,7 +668,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -689,7 +689,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -710,7 +710,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -731,7 +731,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -752,7 +752,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.0+vite@4.0.4 '@tsconfig/svelte': 3.0.0 @@ -773,7 +773,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -797,7 +797,7 @@ importers: typescript: ^4.7.4 vite: ^4.0.0 dependencies: - '@tanstack/svelte-query': link:../../../packages/svelte-query + '@tanstack/svelte-query': 4.24.4_svelte@3.55.0 devDependencies: '@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.7 '@sveltejs/kit': 1.0.7_svelte@3.55.0+vite@4.0.4 @@ -818,7 +818,7 @@ importers: vite: ^3.1.8 vue: ^3.2.41 dependencies: - '@tanstack/vue-query': link:../../../packages/vue-query + '@tanstack/vue-query': 4.24.4_vue@3.2.41 vue: 3.2.41 devDependencies: '@vitejs/plugin-vue': 3.1.2_vite@3.1.8+vue@3.2.41 @@ -833,7 +833,7 @@ importers: vite: 3.1.4 vue: 3.2.39 dependencies: - '@tanstack/vue-query': link:../../../packages/vue-query + '@tanstack/vue-query': 4.24.4_vue@3.2.39 vue: 3.2.39 devDependencies: '@vitejs/plugin-vue': 3.1.0_vite@3.1.4+vue@3.2.39 @@ -850,9 +850,9 @@ importers: vite: 3.2.2 vue: 3.2.41 dependencies: - '@tanstack/query-persist-client-core': link:../../../packages/query-persist-client-core - '@tanstack/query-sync-storage-persister': link:../../../packages/query-sync-storage-persister - '@tanstack/vue-query': link:../../../packages/vue-query + '@tanstack/query-persist-client-core': 4.24.4 + '@tanstack/query-sync-storage-persister': 4.24.4 + '@tanstack/vue-query': 4.24.4_vue@3.2.41 vue: 3.2.41 devDependencies: '@vitejs/plugin-vue': 3.2.0_vite@3.2.2+vue@3.2.41 @@ -1003,22 +1003,24 @@ importers: packages/vue-query: specifiers: - '@tanstack/match-sorter-utils': ^8.1.1 + '@tanstack/match-sorter-utils': ^8.7.6 '@tanstack/query-core': workspace:* '@vue/composition-api': 1.7.1 - '@vue/devtools-api': ^6.4.2 - vue: ^3.2.40 + '@vue/devtools-api': ^6.5.0 + vue: ^3.2.47 vue-demi: ^0.13.11 - vue2: npm:vue@2 + vue2: npm:vue@2.6 + vue2.7: npm:vue@2.7 dependencies: - '@tanstack/match-sorter-utils': 8.1.1 + '@tanstack/match-sorter-utils': 8.7.6 '@tanstack/query-core': link:../query-core - '@vue/devtools-api': 6.4.2 - vue-demi: 0.13.11_zv57lmwz3lpne326jxcwg2uc6q + '@vue/devtools-api': 6.5.0 + vue-demi: 0.13.11_nl35ja3bquglpolb4ogn4n6pq4 devDependencies: - '@vue/composition-api': 1.7.1_vue@3.2.40 - vue: 3.2.40 - vue2: /vue/2.7.10 + '@vue/composition-api': 1.7.1_vue@3.2.47 + vue: 3.2.47 + vue2: /vue/2.6.14 + vue2.7: /vue/2.7.14 packages: @@ -6021,20 +6023,40 @@ packages: tslib: 2.4.1 dev: false - /@tanstack/match-sorter-utils/8.1.1: - resolution: {integrity: sha512-IdmEekEYxQsoLOR0XQyw3jD1GujBpRRYaGJYQUw1eOT1eUugWxdc7jomh1VQ1EKHcdwDLpLaCz/8y4KraU4T9A==} + /@tanstack/eslint-plugin-query/4.24.5: + resolution: {integrity: sha512-KvuRkA9fYRZF9umwpLpWLcEqAVK7IWK8qjmnR3iwPIpkTFNGzaGogs7YkRCZs6q77QxI+c6MWG4xwjLIK/UmdA==} + dev: true + + /@tanstack/match-sorter-utils/8.7.0: + resolution: {integrity: sha512-OgfIPMHTfuw9JGcXCCoEHWFP/eSP2eyhCYwkrFnWBM3NbUPAgOlFP11DbM7cozDRVB0XbPr1tD4pLAtWKlVUVg==} engines: {node: '>=12'} dependencies: remove-accents: 0.4.2 dev: false - /@tanstack/match-sorter-utils/8.7.0: - resolution: {integrity: sha512-OgfIPMHTfuw9JGcXCCoEHWFP/eSP2eyhCYwkrFnWBM3NbUPAgOlFP11DbM7cozDRVB0XbPr1tD4pLAtWKlVUVg==} + /@tanstack/match-sorter-utils/8.7.6: + resolution: {integrity: sha512-2AMpRiA6QivHOUiBpQAVxjiHAA68Ei23ZUMNaRJrN6omWiSFLoYrxGcT6BXtuzp0Jw4h6HZCmGGIM/gbwebO2A==} engines: {node: '>=12'} dependencies: remove-accents: 0.4.2 dev: false + /@tanstack/query-core/4.24.4: + resolution: {integrity: sha512-9dqjv9eeB6VHN7lD3cLo16ZAjfjCsdXetSAD5+VyKqLUvcKTL0CklGQRJu+bWzdrS69R6Ea4UZo8obHYZnG6aA==} + dev: false + + /@tanstack/query-persist-client-core/4.24.4: + resolution: {integrity: sha512-t4BR/th3tu2tkfF0Tcl5z+MbglDjTdIbsLZYlly7l2M6EhGXRjOLbvVUA5Kcx7E2a81Vup0zx0z0wlx+RPGfmg==} + dependencies: + '@tanstack/query-core': 4.24.4 + dev: false + + /@tanstack/query-sync-storage-persister/4.24.4: + resolution: {integrity: sha512-0wffVqoOydMc1TDjOiATv/TM8wJfMpRcM82Cr19TuepListopTsuZ3RzSzLKBCo8WRl/0zCR1Ti9t1zn+Oai/A==} + dependencies: + '@tanstack/query-persist-client-core': 4.24.4 + dev: false + /@tanstack/react-location/3.7.4_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-6rH2vNHGr0uyeUz5ZHvWMYjeYKGgIKFzvs5749QtnS9f+FU7t7fQE0hKZAzltBZk82LT7iYbcHBRyUg2lW13VA==} engines: {node: '>=12'} @@ -6048,6 +6070,132 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /@tanstack/react-query-devtools/4.24.4_bqexmznvdh3tavatamdji3xwki: + resolution: {integrity: sha512-4mldcR99QDX8k94I+STM9gPsYF+FDAD2EQJvHtxR2HrDNegbfmY474xuW0QUZaNW/vJi09Gak6b6Vy2INWhL6w==} + peerDependencies: + '@tanstack/react-query': 4.24.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/match-sorter-utils': 8.7.0 + '@tanstack/react-query': 4.24.4_6xi2cjj4jkvnn2q2msxp65enam + react: 17.0.1 + react-dom: 17.0.1_react@17.0.1 + superjson: 1.10.0 + use-sync-external-store: 1.2.0_react@17.0.1 + dev: false + + /@tanstack/react-query-devtools/4.24.4_pkeil6ml7pq7xvil3imldjs2sa: + resolution: {integrity: sha512-4mldcR99QDX8k94I+STM9gPsYF+FDAD2EQJvHtxR2HrDNegbfmY474xuW0QUZaNW/vJi09Gak6b6Vy2INWhL6w==} + peerDependencies: + '@tanstack/react-query': 4.24.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/match-sorter-utils': 8.7.0 + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + superjson: 1.10.0 + use-sync-external-store: 1.2.0_react@18.2.0 + dev: false + + /@tanstack/react-query-persist-client/4.24.4_pbzrecrhgj7fe7hddoo6lp43hu: + resolution: {integrity: sha512-vQ10ghQrmk+VMrv8BtD1WgvdcGlL7z8QLC9oGryYPORLueOmVvW8nB3GL7aWp84pkLLXPLR32WopXMfseHWukw==} + peerDependencies: + '@tanstack/react-query': 4.24.4 + dependencies: + '@tanstack/query-persist-client-core': 4.24.4 + '@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y + dev: false + + /@tanstack/react-query/4.24.4_6xi2cjj4jkvnn2q2msxp65enam: + resolution: {integrity: sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 4.24.4 + react: 17.0.1 + react-dom: 17.0.1_react@17.0.1 + react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + use-sync-external-store: 1.2.0_react@17.0.1 + dev: false + + /@tanstack/react-query/4.24.4_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 4.24.4 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + use-sync-external-store: 1.2.0_react@18.2.0 + dev: false + + /@tanstack/solid-query/4.24.4_solid-js@1.5.4: + resolution: {integrity: sha512-aAFNJ3liO0JiBwhAiE/1uwvViz0fokCMwUILUlueHuCZ95LDq+I9Pw9pYjoXLyPoFfp36N398AyV8pALZqbDRg==} + peerDependencies: + solid-js: ^1.5.7 + dependencies: + '@tanstack/query-core': 4.24.4 + solid-js: 1.5.4 + dev: false + + /@tanstack/svelte-query/4.24.4_svelte@3.55.0: + resolution: {integrity: sha512-B2N+nNPOe9wxZ01i3Ebvkc8vDX9ZDQTiaazXqpld7O27R+zhIBlk6MU8vNzPBDSKrhhl2kak8SwyJOExsVZQXA==} + peerDependencies: + svelte: ^3.54.0 + dependencies: + '@tanstack/query-core': 4.24.4 + svelte: 3.55.0 + dev: false + + /@tanstack/vue-query/4.24.4_vue@3.2.39: + resolution: {integrity: sha512-ufkdrBDBNIbaXU/djjOLX4ObcANB/WrrNlfUidZL/xd/zGh3VC5ks/Ri2C9Oec0QTgA0HDMqgEMD9ogb0udXdQ==} + peerDependencies: + '@vue/composition-api': ^1.1.2 + vue: ^2.5.0 || ^3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + '@tanstack/match-sorter-utils': 8.7.0 + '@tanstack/query-core': 4.24.4 + '@vue/devtools-api': 6.4.2 + vue: 3.2.39 + vue-demi: 0.13.11_vue@3.2.39 + dev: false + + /@tanstack/vue-query/4.24.4_vue@3.2.41: + resolution: {integrity: sha512-ufkdrBDBNIbaXU/djjOLX4ObcANB/WrrNlfUidZL/xd/zGh3VC5ks/Ri2C9Oec0QTgA0HDMqgEMD9ogb0udXdQ==} + peerDependencies: + '@vue/composition-api': ^1.1.2 + vue: ^2.5.0 || ^3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + '@tanstack/match-sorter-utils': 8.7.0 + '@tanstack/query-core': 4.24.4 + '@vue/devtools-api': 6.4.2 + vue: 3.2.41 + vue-demi: 0.13.11_vue@3.2.41 + dev: false + /@testing-library/dom/7.31.2: resolution: {integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==} engines: {node: '>=10'} @@ -6892,19 +7040,19 @@ packages: estree-walker: 2.0.2 source-map: 0.6.1 - /@vue/compiler-core/3.2.40: - resolution: {integrity: sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==} + /@vue/compiler-core/3.2.41: + resolution: {integrity: sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==} dependencies: '@babel/parser': 7.19.1 - '@vue/shared': 3.2.40 + '@vue/shared': 3.2.41 estree-walker: 2.0.2 source-map: 0.6.1 - /@vue/compiler-core/3.2.41: - resolution: {integrity: sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==} + /@vue/compiler-core/3.2.47: + resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: '@babel/parser': 7.19.1 - '@vue/shared': 3.2.41 + '@vue/shared': 3.2.47 estree-walker: 2.0.2 source-map: 0.6.1 @@ -6921,20 +7069,20 @@ packages: '@vue/compiler-core': 3.2.39 '@vue/shared': 3.2.39 - /@vue/compiler-dom/3.2.40: - resolution: {integrity: sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==} - dependencies: - '@vue/compiler-core': 3.2.40 - '@vue/shared': 3.2.40 - /@vue/compiler-dom/3.2.41: resolution: {integrity: sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw==} dependencies: '@vue/compiler-core': 3.2.41 '@vue/shared': 3.2.41 - /@vue/compiler-sfc/2.7.10: - resolution: {integrity: sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==} + /@vue/compiler-dom/3.2.47: + resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} + dependencies: + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 + + /@vue/compiler-sfc/2.7.14: + resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==} dependencies: '@babel/parser': 7.19.1 postcss: 8.4.21 @@ -6970,20 +7118,6 @@ packages: postcss: 8.4.21 source-map: 0.6.1 - /@vue/compiler-sfc/3.2.40: - resolution: {integrity: sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==} - dependencies: - '@babel/parser': 7.19.1 - '@vue/compiler-core': 3.2.40 - '@vue/compiler-dom': 3.2.40 - '@vue/compiler-ssr': 3.2.40 - '@vue/reactivity-transform': 3.2.40 - '@vue/shared': 3.2.40 - estree-walker: 2.0.2 - magic-string: 0.25.9 - postcss: 8.4.21 - source-map: 0.6.1 - /@vue/compiler-sfc/3.2.41: resolution: {integrity: sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w==} dependencies: @@ -6998,6 +7132,20 @@ packages: postcss: 8.4.21 source-map: 0.6.1 + /@vue/compiler-sfc/3.2.47: + resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} + dependencies: + '@babel/parser': 7.19.1 + '@vue/compiler-core': 3.2.47 + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-ssr': 3.2.47 + '@vue/reactivity-transform': 3.2.47 + '@vue/shared': 3.2.47 + estree-walker: 2.0.2 + magic-string: 0.25.9 + postcss: 8.4.21 + source-map: 0.6.1 + /@vue/compiler-ssr/3.2.37: resolution: {integrity: sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==} dependencies: @@ -7011,29 +7159,33 @@ packages: '@vue/compiler-dom': 3.2.39 '@vue/shared': 3.2.39 - /@vue/compiler-ssr/3.2.40: - resolution: {integrity: sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==} - dependencies: - '@vue/compiler-dom': 3.2.40 - '@vue/shared': 3.2.40 - /@vue/compiler-ssr/3.2.41: resolution: {integrity: sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ==} dependencies: '@vue/compiler-dom': 3.2.41 '@vue/shared': 3.2.41 - /@vue/composition-api/1.7.1_vue@3.2.40: + /@vue/compiler-ssr/3.2.47: + resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/shared': 3.2.47 + + /@vue/composition-api/1.7.1_vue@3.2.47: resolution: {integrity: sha512-xDWoEtxGXhH9Ku3ROYX/rzhcpt4v31hpPU5zF3UeVC/qxA3dChmqU8zvTUYoKh3j7rzpNsoFOwqsWG7XPMlaFA==} peerDependencies: vue: '>= 2.5 < 2.7' dependencies: - vue: 3.2.40 + vue: 3.2.47 /@vue/devtools-api/6.4.2: resolution: {integrity: sha512-6hNZ23h1M2Llky+SIAmVhL7s6BjLtZBCzjIz9iRSBUsysjE7kC39ulW0dH4o/eZtycmSt4qEr6RDVGTIuWu+ow==} dev: false + /@vue/devtools-api/6.5.0: + resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} + dev: false + /@vue/reactivity-transform/3.2.37: resolution: {integrity: sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==} dependencies: @@ -7053,21 +7205,21 @@ packages: estree-walker: 2.0.2 magic-string: 0.25.9 - /@vue/reactivity-transform/3.2.40: - resolution: {integrity: sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==} + /@vue/reactivity-transform/3.2.41: + resolution: {integrity: sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==} dependencies: '@babel/parser': 7.19.1 - '@vue/compiler-core': 3.2.40 - '@vue/shared': 3.2.40 + '@vue/compiler-core': 3.2.41 + '@vue/shared': 3.2.41 estree-walker: 2.0.2 magic-string: 0.25.9 - /@vue/reactivity-transform/3.2.41: - resolution: {integrity: sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==} + /@vue/reactivity-transform/3.2.47: + resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: '@babel/parser': 7.19.1 - '@vue/compiler-core': 3.2.41 - '@vue/shared': 3.2.41 + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 estree-walker: 2.0.2 magic-string: 0.25.9 @@ -7082,16 +7234,16 @@ packages: dependencies: '@vue/shared': 3.2.39 - /@vue/reactivity/3.2.40: - resolution: {integrity: sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==} - dependencies: - '@vue/shared': 3.2.40 - /@vue/reactivity/3.2.41: resolution: {integrity: sha512-9JvCnlj8uc5xRiQGZ28MKGjuCoPhhTwcoAdv3o31+cfGgonwdPNuvqAXLhlzu4zwqavFEG5tvaoINQEfxz+l6g==} dependencies: '@vue/shared': 3.2.41 + /@vue/reactivity/3.2.47: + resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} + dependencies: + '@vue/shared': 3.2.47 + /@vue/runtime-core/3.2.37: resolution: {integrity: sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==} dependencies: @@ -7105,18 +7257,18 @@ packages: '@vue/reactivity': 3.2.39 '@vue/shared': 3.2.39 - /@vue/runtime-core/3.2.40: - resolution: {integrity: sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==} - dependencies: - '@vue/reactivity': 3.2.40 - '@vue/shared': 3.2.40 - /@vue/runtime-core/3.2.41: resolution: {integrity: sha512-0LBBRwqnI0p4FgIkO9q2aJBBTKDSjzhnxrxHYengkAF6dMOjeAIZFDADAlcf2h3GDALWnblbeprYYpItiulSVQ==} dependencies: '@vue/reactivity': 3.2.41 '@vue/shared': 3.2.41 + /@vue/runtime-core/3.2.47: + resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==} + dependencies: + '@vue/reactivity': 3.2.47 + '@vue/shared': 3.2.47 + /@vue/runtime-dom/3.2.37: resolution: {integrity: sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==} dependencies: @@ -7132,13 +7284,6 @@ packages: '@vue/shared': 3.2.39 csstype: 2.6.20 - /@vue/runtime-dom/3.2.40: - resolution: {integrity: sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==} - dependencies: - '@vue/runtime-core': 3.2.40 - '@vue/shared': 3.2.40 - csstype: 2.6.20 - /@vue/runtime-dom/3.2.41: resolution: {integrity: sha512-U7zYuR1NVIP8BL6jmOqmapRAHovEFp7CSw4pR2FacqewXNGqZaRfHoNLQsqQvVQ8yuZNZtxSZy0FFyC70YXPpA==} dependencies: @@ -7146,6 +7291,13 @@ packages: '@vue/shared': 3.2.41 csstype: 2.6.20 + /@vue/runtime-dom/3.2.47: + resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==} + dependencies: + '@vue/runtime-core': 3.2.47 + '@vue/shared': 3.2.47 + csstype: 2.6.20 + /@vue/server-renderer/3.2.37_vue@3.2.37: resolution: {integrity: sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==} peerDependencies: @@ -7165,15 +7317,6 @@ packages: '@vue/shared': 3.2.39 vue: 3.2.39 - /@vue/server-renderer/3.2.40_vue@3.2.40: - resolution: {integrity: sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==} - peerDependencies: - vue: 3.2.40 - dependencies: - '@vue/compiler-ssr': 3.2.40 - '@vue/shared': 3.2.40 - vue: 3.2.40 - /@vue/server-renderer/3.2.41_vue@3.2.41: resolution: {integrity: sha512-7YHLkfJdTlsZTV0ae5sPwl9Gn/EGr2hrlbcS/8naXm2CDpnKUwC68i1wGlrYAfIgYWL7vUZwk2GkYLQH5CvFig==} peerDependencies: @@ -7183,6 +7326,15 @@ packages: '@vue/shared': 3.2.41 vue: 3.2.41 + /@vue/server-renderer/3.2.47_vue@3.2.47: + resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==} + peerDependencies: + vue: 3.2.47 + dependencies: + '@vue/compiler-ssr': 3.2.47 + '@vue/shared': 3.2.47 + vue: 3.2.47 + /@vue/shared/3.2.37: resolution: {integrity: sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==} dev: true @@ -7190,12 +7342,12 @@ packages: /@vue/shared/3.2.39: resolution: {integrity: sha512-D3dl2ZB9qE6mTuWPk9RlhDeP1dgNRUKC3NJxji74A4yL8M2MwlhLKUC/49WHjrNzSPug58fWx/yFbaTzGAQSBw==} - /@vue/shared/3.2.40: - resolution: {integrity: sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==} - /@vue/shared/3.2.41: resolution: {integrity: sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw==} + /@vue/shared/3.2.47: + resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} + /@xmldom/xmldom/0.7.5: resolution: {integrity: sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==} engines: {node: '>=10.0.0'} @@ -16216,7 +16368,6 @@ packages: /svelte/3.55.0: resolution: {integrity: sha512-uGu2FVMlOuey4JoKHKrpZFkoYyj0VLjJdz47zX5+gVK5odxHM40RVhar9/iK2YFRVxvfg9FkhfVlR0sjeIrOiA==} engines: {node: '>= 8'} - dev: true /svelte2tsx/0.6.0_glsdxddlaertg66rhhvanbinpy: resolution: {integrity: sha512-TrxfQkO7CKi8Pu2eC/FyteDCdk3OOeQV5u6z7OjYAsOhsd0ClzAKqxJdvp6xxNQLrbFzf/XvCi9Fy8MQ1MleFA==} @@ -16909,6 +17060,22 @@ packages: react: 18.2.0 dev: false + /use-sync-external-store/1.2.0_react@17.0.1: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.1 + dev: false + + /use-sync-external-store/1.2.0_react@18.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /use/3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} @@ -17260,7 +17427,23 @@ packages: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} dev: false - /vue-demi/0.13.11_zv57lmwz3lpne326jxcwg2uc6q: + /vue-demi/0.13.11_nl35ja3bquglpolb4ogn4n6pq4: + resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + '@vue/composition-api': 1.7.1_vue@3.2.47 + vue: 3.2.47 + dev: false + + /vue-demi/0.13.11_vue@3.2.39: resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} engines: {node: '>=12'} hasBin: true @@ -17272,14 +17455,32 @@ packages: '@vue/composition-api': optional: true dependencies: - '@vue/composition-api': 1.7.1_vue@3.2.40 - vue: 3.2.40 + vue: 3.2.39 dev: false - /vue/2.7.10: - resolution: {integrity: sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==} + /vue-demi/0.13.11_vue@3.2.41: + resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.2.41 + dev: false + + /vue/2.6.14: + resolution: {integrity: sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==} + dev: true + + /vue/2.7.14: + resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==} dependencies: - '@vue/compiler-sfc': 2.7.10 + '@vue/compiler-sfc': 2.7.14 csstype: 3.1.0 dev: true @@ -17302,15 +17503,6 @@ packages: '@vue/server-renderer': 3.2.39_vue@3.2.39 '@vue/shared': 3.2.39 - /vue/3.2.40: - resolution: {integrity: sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==} - dependencies: - '@vue/compiler-dom': 3.2.40 - '@vue/compiler-sfc': 3.2.40 - '@vue/runtime-dom': 3.2.40 - '@vue/server-renderer': 3.2.40_vue@3.2.40 - '@vue/shared': 3.2.40 - /vue/3.2.41: resolution: {integrity: sha512-uuuvnrDXEeZ9VUPljgHkqB5IaVO8SxhPpqF2eWOukVrBnRBx2THPSGQBnVRt0GrIG1gvCmFXMGbd7FqcT1ixNQ==} dependencies: @@ -17320,6 +17512,15 @@ packages: '@vue/server-renderer': 3.2.41_vue@3.2.41 '@vue/shared': 3.2.41 + /vue/3.2.47: + resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/runtime-dom': 3.2.47 + '@vue/server-renderer': 3.2.47_vue@3.2.47 + '@vue/shared': 3.2.47 + /w3c-hr-time/1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: From aee932b3a1256ec73e56682f846c00e934b045fb Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 12 Feb 2023 14:38:11 +0100 Subject: [PATCH 033/314] feat(types): TError can now globally be defaulted / registered (#4903) * types: TError can now globally be defaulted / registered * docs: add DefaultError * test: stabilize a flaky test --- docs/react/guides/migrating-to-v5.md | 4 +- docs/react/typescript.md | 47 +++++++--- docs/vue/typescript.md | 2 +- .../query-core/src/infiniteQueryObserver.ts | 3 +- packages/query-core/src/mutation.ts | 11 ++- packages/query-core/src/mutationCache.ts | 9 +- packages/query-core/src/mutationObserver.ts | 3 +- packages/query-core/src/query.ts | 7 +- packages/query-core/src/queryCache.ts | 11 ++- packages/query-core/src/queryClient.ts | 13 +-- packages/query-core/src/queryObserver.ts | 8 +- packages/query-core/src/retryer.ts | 10 +-- packages/query-core/src/types.ts | 89 ++++++++++++------- .../src/__tests__/useQuery.test.tsx | 16 ++-- packages/react-query/src/types.ts | 27 +++--- packages/react-query/src/useInfiniteQuery.ts | 9 +- packages/react-query/src/useMutation.ts | 4 +- packages/react-query/src/useQueries.ts | 5 +- packages/react-query/src/useQuery.ts | 16 ++-- .../src/__tests__/createQuery.test.tsx | 14 +-- .../solid-query/src/createInfiniteQuery.ts | 9 +- packages/solid-query/src/createMutation.ts | 4 +- packages/solid-query/src/createQueries.ts | 5 +- packages/solid-query/src/createQuery.ts | 16 ++-- packages/solid-query/src/types.ts | 33 +++---- .../svelte-query/src/createInfiniteQuery.ts | 9 +- packages/svelte-query/src/createMutation.ts | 4 +- packages/svelte-query/src/createQueries.ts | 5 +- packages/svelte-query/src/createQuery.ts | 16 ++-- packages/svelte-query/src/types.ts | 31 ++++--- packages/vue-query/src/mutationCache.ts | 13 ++- packages/vue-query/src/queryCache.ts | 9 +- packages/vue-query/src/queryClient.ts | 19 ++-- packages/vue-query/src/useInfiniteQuery.ts | 5 +- packages/vue-query/src/useMutation.ts | 7 +- packages/vue-query/src/useQuery.ts | 9 +- 36 files changed, 318 insertions(+), 184 deletions(-) diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index c7a5badc77..d5fdd9826a 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -167,7 +167,7 @@ const queryClient = new QueryClient({ To make the `useErrorBoundary` option more framework-agnostic and avoid confusion with the established React function prefix "`use`" for hooks and the "ErrorBoundary" component name, it has been renamed to `throwErrors` to more accurately reflect its functionality. -### `Error` is now the default type for errors instead of `unknown` +### TypeScript: `Error` is now the default type for errors instead of `unknown` Even though in JavaScript, you can `throw` anything (which makes `unknown` the most correct type), almost always, `Errors` (or subclasses of `Error`) are thrown. This change makes it easier to work with the `error` field in TypeScript for most cases. @@ -185,6 +185,8 @@ useQuery({ }) ``` +For a way to set a different kind of Error globally, see [the TypeScript Guide](../typescript#registering-a-global-error). + ### eslint `prefer-query-object-syntax` rule is removed Since the only supported syntax now is the object syntax, this rule is no longer needed diff --git a/docs/react/typescript.md b/docs/react/typescript.md index 20e4346bf5..10752d6fe4 100644 --- a/docs/react/typescript.md +++ b/docs/react/typescript.md @@ -85,34 +85,59 @@ if (isSuccess) { ## Typing the error field -The type for error defaults to `unknown`. This is in line with what TypeScript gives you per default in a catch clauses (see [useUnknownInCatchVariables](https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#use-unknown-catch-variables)). The safest way to work with `error` would be to perform a runtime check; another way would be to explicitly define types for `data` and `error`: +The type for error defaults to `Error`, because that is what most users expect. ```tsx const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) -// ^? const error: unknown - -if (error instanceof Error) { - error - // ^? const error: Error -} +// ^? const error: Error ``` [//]: # 'Playground5' -[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFCiSw4dAB7AIqUuUpURY1Nx68YeMOjgBxcsjBwAvIjjAAJgC44AO2QgARriK9eDCOdTwS6GAwAWmiNon6ABQAlGYAClLAGAA8vtoA2gC6AHx6qbLiAHQA5h6BVAD02Vpg8sGZMF7o5oG0qJAuarqpdQ0YmUZ0MHTBDjxOLvBIuORQRHooGNi4eIHxVMV+pVSJADSkHt5xpb08BQVwh0cAegD8fcAkcIEj0IaDdOYM6BBXAKJQo8GIvIe3ULx9nAzrxCEA) +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFZVLWR9VQ5ADSkwWGZ9WOSnJxwl1cAegD8QA) [//]: # 'Playground5' +If you want to throw a custom error, or something that isn't an Error at all, you can specify the type of the error field: + ```tsx -const { error } = useQuery(['groups'], fetchGroups) +const { error } = useQuery(['groups'], fetchGroups) +// ^? const error: string | null +``` + +However, this has the drawback that type inference for all other generics of `useQuery` will not work anymore. It is generally not considered a good practice to throw something that isn't and `Error`, so if you have a subclass like `AxisError` you can use _type narrowing_ to make the error field more specific: + +```tsx +import axios from 'axios' + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) // ^? const error: Error | null + +if (axios.isAxiosError(error)) { + error + // ^? const error: AxiosError +} ``` +[//]: # 'Playground6' +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) [//]: # 'Playground6' -[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFCiSw4dAB7AIqUuUpURY1Nx68YeMOjgBxcsjBwAvIjjAAJgC44AO2QgARriK9eDCOdTwS6GAwAWmiNon6ABQAlGYAClLAGAA8vtoA2gC6AHx6qbLiAHQA5h6BVAD02Vpg8sGZMF7o5oG0qJAuarqpdQ0YmUZ0MHTBDjxOLvBIuORQRHooGNi4eLElSQA0cACiUKPJgfFUxX6lVIlL7p4+Jai9PAUFcNc3AHoA-LxAA) +### Registering a global Error + +TanStack Query v5 allows for a way to set a global Error type for everything, without having to specify generics on call-sides, by amending the `Register` interface. This will make sure inference still works, but the error field will be of the specified type: + +```tsx +declare module '@tanstack/react-query' { + interface Register { + defaultError: AxiosError + } +} + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: AxiosError | null +``` -[//]: # 'Playground6' [//]: # 'Materials' ## Further Reading diff --git a/docs/vue/typescript.md b/docs/vue/typescript.md index e0522bf411..c8e0a577a2 100644 --- a/docs/vue/typescript.md +++ b/docs/vue/typescript.md @@ -33,7 +33,7 @@ replace: [//]: # 'Playground5' [//]: # 'Playground6' -[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUKEiw49AB7AIqUuUpV5i1GPESYeMOjgBxcsjBwAvIjjAAJgC44jZCABGuIhImsIzeCXQYVgALEwgzZSsACgBKRwAFVWAMAB4wswBtAF0APks8jSUAOgBzQKiqThLTMC0Yophg9EYoqHRUSGZDCzy2jt8MItt6BhivcR8-a1xyKCJLFAxsXDw0muyAGjgAUShZnKiMqmrw2qosrYCg0JrUMfFOTjhnl4A9AH4JIA) +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) [//]: # 'Playground6' [//]: # 'Materials' diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index e51533d92e..76f4e8eeb7 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -6,6 +6,7 @@ import type { InfiniteQueryObserverOptions, InfiniteQueryObserverResult, QueryKey, + RegisteredError, } from './types' import type { QueryClient } from './queryClient' import type { NotifyOptions, ObserverFetchOptions } from './queryObserver' @@ -23,7 +24,7 @@ type InfiniteQueryObserverListener = ( export class InfiniteQueryObserver< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 1782cd2148..1c8aeab7b6 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -1,4 +1,9 @@ -import type { MutationOptions, MutationStatus, MutationMeta } from './types' +import type { + MutationOptions, + MutationStatus, + MutationMeta, + RegisteredError, +} from './types' import type { MutationCache } from './mutationCache' import type { MutationObserver } from './mutationObserver' import { notifyManager } from './notifyManager' @@ -19,7 +24,7 @@ interface MutationConfig { export interface MutationState< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > { @@ -75,7 +80,7 @@ export type Action = export class Mutation< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends Removable { diff --git a/packages/query-core/src/mutationCache.ts b/packages/query-core/src/mutationCache.ts index f22303e116..30c7490c56 100644 --- a/packages/query-core/src/mutationCache.ts +++ b/packages/query-core/src/mutationCache.ts @@ -1,5 +1,5 @@ import type { MutationObserver } from './mutationObserver' -import type { MutationOptions } from './types' +import type { MutationOptions, RegisteredError } from './types' import type { QueryClient } from './queryClient' import { notifyManager } from './notifyManager' import type { Action, MutationState } from './mutation' @@ -127,7 +127,12 @@ export class MutationCache extends Subscribable { return this.#mutations } - find( + find< + TData = unknown, + TError = RegisteredError, + TVariables = any, + TContext = unknown, + >( filters: MutationFilters, ): Mutation | undefined { if (typeof filters.exact === 'undefined') { diff --git a/packages/query-core/src/mutationObserver.ts b/packages/query-core/src/mutationObserver.ts index f4b0c700b2..0246d29564 100644 --- a/packages/query-core/src/mutationObserver.ts +++ b/packages/query-core/src/mutationObserver.ts @@ -8,6 +8,7 @@ import type { MutationObserverBaseResult, MutationObserverResult, MutationObserverOptions, + RegisteredError, } from './types' import { shallowEqualObjects } from './utils' @@ -26,7 +27,7 @@ interface NotifyOptions { export class MutationObserver< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends Subscribable< diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 100431549c..be8228c7f7 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -9,6 +9,7 @@ import type { CancelOptions, SetDataOptions, FetchStatus, + RegisteredError, } from './types' import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' @@ -33,7 +34,7 @@ interface QueryConfig< state?: QueryState } -export interface QueryState { +export interface QueryState { data: TData | undefined dataUpdateCount: number dataUpdatedAt: number @@ -64,7 +65,7 @@ export interface FetchContext< export interface QueryBehavior< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > { @@ -137,7 +138,7 @@ export interface SetStateOptions { export class Query< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends Removable { diff --git a/packages/query-core/src/queryCache.ts b/packages/query-core/src/queryCache.ts index 0a38ec58fe..d371fa6cc6 100644 --- a/packages/query-core/src/queryCache.ts +++ b/packages/query-core/src/queryCache.ts @@ -2,7 +2,12 @@ import type { QueryFilters } from './utils' import { hashQueryKeyByOptions, matchQuery } from './utils' import type { Action, QueryState } from './query' import { Query } from './query' -import type { QueryKey, QueryOptions, WithRequired } from './types' +import type { + QueryKey, + QueryOptions, + RegisteredError, + WithRequired, +} from './types' import { notifyManager } from './notifyManager' import type { QueryClient } from './queryClient' import { Subscribable } from './subscribable' @@ -134,7 +139,7 @@ export class QueryCache extends Subscribable { get< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -149,7 +154,7 @@ export class QueryCache extends Subscribable { return [...this.#queries.values()] } - find( + find( filters: WithRequired, ): Query | undefined { if (typeof filters.exact === 'undefined') { diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 55db458d9f..be8fbf2ca5 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -25,6 +25,7 @@ import type { ResetOptions, ResetQueryFilters, SetDataOptions, + RegisteredError, } from './types' import type { QueryState } from './query' import { QueryCache } from './queryCache' @@ -177,7 +178,7 @@ export class QueryClient { ) } - getQueryState( + getQueryState( queryKey: QueryKey, ): QueryState | undefined { return this.#queryCache.find({ queryKey })?.state @@ -276,7 +277,7 @@ export class QueryClient { fetchQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -298,7 +299,7 @@ export class QueryClient { prefetchQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -309,7 +310,7 @@ export class QueryClient { fetchInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -321,7 +322,7 @@ export class QueryClient { prefetchInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -405,7 +406,7 @@ export class QueryClient { defaultQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index f5ecc0bda6..f34d878b15 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -1,4 +1,8 @@ -import type { DefaultedQueryObserverOptions, RefetchPageFilters } from './types' +import type { + DefaultedQueryObserverOptions, + RefetchPageFilters, + RegisteredError, +} from './types' import { isServer, isValidTimeout, @@ -39,7 +43,7 @@ export interface ObserverFetchOptions extends FetchOptions { export class QueryObserver< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index edf3241cbd..b9b82ec181 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -1,11 +1,11 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { sleep } from './utils' -import type { CancelOptions, NetworkMode } from './types' +import type { CancelOptions, NetworkMode, RegisteredError } from './types' // TYPES -interface RetryerConfig { +interface RetryerConfig { fn: () => TData | Promise abort?: () => void onError?: (error: TError) => void @@ -28,14 +28,14 @@ export interface Retryer { export type RetryValue = boolean | number | ShouldRetryFunction -type ShouldRetryFunction = ( +type ShouldRetryFunction = ( failureCount: number, error: TError, ) => boolean export type RetryDelayValue = number | RetryDelayFunction -type RetryDelayFunction = ( +type RetryDelayFunction = ( failureCount: number, error: TError, ) => number @@ -63,7 +63,7 @@ export function isCancelledError(value: any): value is CancelledError { return value instanceof CancelledError } -export function createRetryer( +export function createRetryer( config: RetryerConfig, ): Retryer { let isRetryCancelled = false diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 6ba53d6c12..7097977d48 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -7,6 +7,16 @@ import type { QueryFilters, QueryTypeFilter } from './utils' import type { QueryCache } from './queryCache' import type { MutationCache } from './mutationCache' +export interface Register { + // defaultError: Error +} + +export type RegisteredError = Register extends { + defaultError: infer TError +} + ? TError + : Error + export type QueryKey = readonly unknown[] export type QueryFunction< @@ -63,7 +73,7 @@ export type NetworkMode = 'online' | 'always' | 'offlineFirst' export interface QueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > { @@ -124,7 +134,7 @@ export type ThrowErrors< export interface QueryObserverOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -252,7 +262,7 @@ export type WithRequired = Omit & Required> export type DefaultedQueryObserverOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -263,7 +273,7 @@ export type DefaultedQueryObserverOptions< export interface InfiniteQueryObserverOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -277,7 +287,7 @@ export interface InfiniteQueryObserverOptions< export type DefaultedInfiniteQueryObserverOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -294,7 +304,7 @@ export type DefaultedInfiniteQueryObserverOptions< export interface FetchQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -310,7 +320,7 @@ export interface FetchQueryOptions< export interface FetchInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends FetchQueryOptions< @@ -366,7 +376,10 @@ export interface FetchPreviousPageOptions extends ResultOptions { export type QueryStatus = 'loading' | 'error' | 'success' export type FetchStatus = 'fetching' | 'paused' | 'idle' -export interface QueryObserverBaseResult { +export interface QueryObserverBaseResult< + TData = unknown, + TError = RegisteredError, +> { data: TData | undefined dataUpdatedAt: number error: TError | null @@ -394,8 +407,10 @@ export interface QueryObserverBaseResult { fetchStatus: FetchStatus } -export interface QueryObserverLoadingResult - extends QueryObserverBaseResult { +export interface QueryObserverLoadingResult< + TData = unknown, + TError = RegisteredError, +> extends QueryObserverBaseResult { data: undefined error: null isError: false @@ -408,7 +423,7 @@ export interface QueryObserverLoadingResult export interface QueryObserverLoadingErrorResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends QueryObserverBaseResult { data: undefined error: TError @@ -422,7 +437,7 @@ export interface QueryObserverLoadingErrorResult< export interface QueryObserverRefetchErrorResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends QueryObserverBaseResult { data: TData error: TError @@ -434,8 +449,10 @@ export interface QueryObserverRefetchErrorResult< status: 'error' } -export interface QueryObserverSuccessResult - extends QueryObserverBaseResult { +export interface QueryObserverSuccessResult< + TData = unknown, + TError = RegisteredError, +> extends QueryObserverBaseResult { data: TData error: null isError: false @@ -446,18 +463,21 @@ export interface QueryObserverSuccessResult status: 'success' } -export type DefinedQueryObserverResult = +export type DefinedQueryObserverResult< + TData = unknown, + TError = RegisteredError, +> = | QueryObserverRefetchErrorResult | QueryObserverSuccessResult -export type QueryObserverResult = +export type QueryObserverResult = | DefinedQueryObserverResult | QueryObserverLoadingErrorResult | QueryObserverLoadingResult export interface InfiniteQueryObserverBaseResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends QueryObserverBaseResult, TError> { fetchNextPage: ( options?: FetchNextPageOptions, @@ -473,7 +493,7 @@ export interface InfiniteQueryObserverBaseResult< export interface InfiniteQueryObserverLoadingResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends InfiniteQueryObserverBaseResult { data: undefined error: null @@ -487,7 +507,7 @@ export interface InfiniteQueryObserverLoadingResult< export interface InfiniteQueryObserverLoadingErrorResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends InfiniteQueryObserverBaseResult { data: undefined error: TError @@ -501,7 +521,7 @@ export interface InfiniteQueryObserverLoadingErrorResult< export interface InfiniteQueryObserverRefetchErrorResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends InfiniteQueryObserverBaseResult { data: InfiniteData error: TError @@ -515,7 +535,7 @@ export interface InfiniteQueryObserverRefetchErrorResult< export interface InfiniteQueryObserverSuccessResult< TData = unknown, - TError = Error, + TError = RegisteredError, > extends InfiniteQueryObserverBaseResult { data: InfiniteData error: null @@ -527,7 +547,10 @@ export interface InfiniteQueryObserverSuccessResult< status: 'success' } -export type InfiniteQueryObserverResult = +export type InfiniteQueryObserverResult< + TData = unknown, + TError = RegisteredError, +> = | InfiniteQueryObserverLoadingErrorResult | InfiniteQueryObserverLoadingResult | InfiniteQueryObserverRefetchErrorResult @@ -547,7 +570,7 @@ export type MutationFunction = ( export interface MutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > { @@ -583,7 +606,7 @@ export interface MutationOptions< export interface MutationObserverOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationOptions { @@ -592,7 +615,7 @@ export interface MutationObserverOptions< export interface MutateOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > { @@ -612,7 +635,7 @@ export interface MutateOptions< export type MutateFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = ( @@ -622,7 +645,7 @@ export type MutateFunction< export interface MutationObserverBaseResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationState { @@ -636,7 +659,7 @@ export interface MutationObserverBaseResult< export interface MutationObserverIdleResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -651,7 +674,7 @@ export interface MutationObserverIdleResult< export interface MutationObserverLoadingResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -666,7 +689,7 @@ export interface MutationObserverLoadingResult< export interface MutationObserverErrorResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -681,7 +704,7 @@ export interface MutationObserverErrorResult< export interface MutationObserverSuccessResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends MutationObserverBaseResult { @@ -696,7 +719,7 @@ export interface MutationObserverSuccessResult< export type MutationObserverResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = @@ -711,7 +734,7 @@ export interface QueryClientConfig { defaultOptions?: DefaultOptions } -export interface DefaultOptions { +export interface DefaultOptions { queries?: QueryObserverOptions mutations?: MutationObserverOptions } diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 0e65f721ea..cb7b368a7c 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -3817,19 +3817,23 @@ describe('useQuery', () => { function Page() { const result = useQuery({ queryKey: key, - queryFn: () => 'serverData', - initialData: 'data', + queryFn: async () => { + await sleep(10) + return 'serverData' + }, + initialData: 'initialData', }) results.push(result) - return null + return
    data: {result.data}
    } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await sleep(10) + await waitFor(() => rendered.getByText('data: initialData')) + await waitFor(() => rendered.getByText('data: serverData')) expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ data: 'data', isFetching: true }) + expect(results[0]).toMatchObject({ data: 'initialData', isFetching: true }) expect(results[1]).toMatchObject({ data: 'serverData', isFetching: false }) }) diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 163924cde9..bbf435976d 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -11,11 +11,12 @@ import type { MutateFunction, DefinedQueryObserverResult, WithRequired, + RegisteredError, } from '@tanstack/query-core' export interface UseBaseQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -26,7 +27,7 @@ export interface UseBaseQueryOptions< export interface UseQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -36,7 +37,7 @@ export interface UseQueryOptions< export interface UseInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -53,32 +54,32 @@ export interface UseInfiniteQueryOptions< export type UseBaseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = QueryObserverResult export type UseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = UseBaseQueryResult export type DefinedUseBaseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = DefinedQueryObserverResult export type DefinedUseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = DefinedUseBaseQueryResult export type UseInfiniteQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = InfiniteQueryObserverResult export interface UseMutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends Omit< @@ -88,7 +89,7 @@ export interface UseMutationOptions< export type UseMutateFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = ( @@ -97,14 +98,14 @@ export type UseMutateFunction< export type UseMutateAsyncFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = MutateFunction export type UseBaseMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > = Override< @@ -114,7 +115,7 @@ export type UseBaseMutationResult< export type UseMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > = UseBaseMutationResult diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index f0ab16e2de..afa27c7801 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -1,4 +1,9 @@ -import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' +import type { + QueryObserver, + QueryKey, + QueryClient, + RegisteredError, +} from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' import { useBaseQuery } from './useBaseQuery' @@ -6,7 +11,7 @@ import { useBaseQuery } from './useBaseQuery' // HOOK export function useInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/react-query/src/useMutation.ts b/packages/react-query/src/useMutation.ts index c6621ccba7..3dfa280fc2 100644 --- a/packages/react-query/src/useMutation.ts +++ b/packages/react-query/src/useMutation.ts @@ -1,5 +1,5 @@ import * as React from 'react' -import type { QueryClient } from '@tanstack/query-core' +import type { QueryClient, RegisteredError } from '@tanstack/query-core' import { notifyManager, MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { @@ -13,7 +13,7 @@ import { shouldThrowError } from './utils' export function useMutation< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, >( diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index be34518f02..b49fcbc026 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -5,6 +5,7 @@ import type { QueryFunction, QueriesPlaceholderDataFunction, QueryClient, + RegisteredError, } from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' @@ -27,7 +28,7 @@ import { // `placeholderData` function does not have a parameter type UseQueryOptionsForUseQueries< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = Omit< @@ -149,7 +150,7 @@ export type QueriesResults< ? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results UseQueryResult< unknown extends TData ? TQueryFnData : TData, - unknown extends TError ? Error : TError + unknown extends TError ? RegisteredError : TError >[] : // Fallback UseQueryResult[] diff --git a/packages/react-query/src/useQuery.ts b/packages/react-query/src/useQuery.ts index df49edee9d..6dbe3f409e 100644 --- a/packages/react-query/src/useQuery.ts +++ b/packages/react-query/src/useQuery.ts @@ -1,4 +1,8 @@ -import type { QueryClient, QueryKey } from '@tanstack/query-core' +import type { + QueryClient, + QueryKey, + RegisteredError, +} from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' import type { DefinedUseQueryResult, @@ -10,7 +14,7 @@ import { useBaseQuery } from './useBaseQuery' // HOOK type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -19,7 +23,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { @@ -28,7 +32,7 @@ type DefinedInitialDataOptions< export function useQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -38,7 +42,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -48,7 +52,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index b495dc50e4..8b76cc9a48 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -3803,15 +3803,18 @@ describe('createQuery', () => { function Page() { const result = createQuery(() => ({ queryKey: key, - queryFn: () => 'serverData', - initialData: 'data', + queryFn: async () => { + await sleep(10) + return 'serverData' + }, + initialData: 'initialData', })) createRenderEffect(() => { results.push({ ...result }) }) - return null + return
    data: {result.data}
    } render(() => ( @@ -3820,10 +3823,11 @@ describe('createQuery', () => { )) - await sleep(10) + await waitFor(() => screen.getByText('data: initialData')) + await waitFor(() => screen.getByText('data: serverData')) expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ data: 'data', isFetching: true }) + expect(results[0]).toMatchObject({ data: 'initialData', isFetching: true }) expect(results[1]).toMatchObject({ data: 'serverData', isFetching: false }) }) diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts index 01b2e57cd3..b4ed787c7a 100644 --- a/packages/solid-query/src/createInfiniteQuery.ts +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -1,4 +1,9 @@ -import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' +import type { + QueryObserver, + QueryKey, + QueryClient, + RegisteredError, +} from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, @@ -9,7 +14,7 @@ import { createMemo } from 'solid-js' export function createInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts index ca19c19287..2fbf59fa2c 100644 --- a/packages/solid-query/src/createMutation.ts +++ b/packages/solid-query/src/createMutation.ts @@ -1,4 +1,4 @@ -import type { QueryClient } from '@tanstack/query-core' +import type { QueryClient, RegisteredError } from '@tanstack/query-core' import { MutationObserver } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import type { @@ -13,7 +13,7 @@ import { shouldThrowError } from './utils' // HOOK export function createMutation< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, >( diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index 410b6d15a1..cc48fa3283 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -3,6 +3,7 @@ import type { QueryClient, QueryFunction, QueryKey, + RegisteredError, } from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' import { createComputed, onCleanup, onMount } from 'solid-js' @@ -14,7 +15,7 @@ import type { CreateQueryResult, SolidQueryOptions } from './types' // `placeholderData` function does not have a parameter type CreateQueryOptionsForCreateQueries< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = Omit< @@ -141,7 +142,7 @@ export type QueriesResults< ? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results CreateQueryResult< unknown extends TData ? TQueryFnData : TData, - unknown extends TError ? Error : TError + unknown extends TError ? RegisteredError : TError >[] : // Fallback CreateQueryResult[] diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts index 9a00aba475..db3fc498a1 100644 --- a/packages/solid-query/src/createQuery.ts +++ b/packages/solid-query/src/createQuery.ts @@ -1,4 +1,8 @@ -import type { QueryClient, QueryKey } from '@tanstack/query-core' +import type { + QueryClient, + QueryKey, + RegisteredError, +} from '@tanstack/query-core' import { QueryObserver } from '@tanstack/query-core' import { createMemo } from 'solid-js' import { createBaseQuery } from './createBaseQuery' @@ -12,7 +16,7 @@ import type { type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -23,7 +27,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -34,7 +38,7 @@ type DefinedInitialDataOptions< export function createQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -44,7 +48,7 @@ export function createQuery< export function createQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -53,7 +57,7 @@ export function createQuery< ): DefinedCreateQueryResult export function createQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index f803dff443..abe11e7098 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -11,13 +11,14 @@ import type { InfiniteQueryObserverOptions, InfiniteQueryObserverResult, WithRequired, + RegisteredError, } from '@tanstack/query-core' export type FunctionedParams = () => T export interface CreateBaseQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -28,7 +29,7 @@ export interface CreateBaseQueryOptions< export interface SolidQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends WithRequired< @@ -44,7 +45,7 @@ export interface SolidQueryOptions< export type CreateQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams> @@ -53,28 +54,28 @@ export type CreateQueryOptions< export type CreateBaseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = QueryObserverResult export type CreateQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = CreateBaseQueryResult export type DefinedCreateBaseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = DefinedQueryObserverResult export type DefinedCreateQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = DefinedCreateBaseQueryResult /* --- Create Infinite Queries Types --- */ export interface SolidInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -93,7 +94,7 @@ export interface SolidInfiniteQueryOptions< export type CreateInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = FunctionedParams< @@ -108,13 +109,13 @@ export type CreateInfiniteQueryOptions< export type CreateInfiniteQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = InfiniteQueryObserverResult /* --- Create Mutation Types --- */ export interface SolidMutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends Omit< @@ -124,14 +125,14 @@ export interface SolidMutationOptions< export type CreateMutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = FunctionedParams> export type CreateMutateFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = ( @@ -140,14 +141,14 @@ export type CreateMutateFunction< export type CreateMutateAsyncFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = MutateFunction export type CreateBaseMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > = Override< @@ -159,7 +160,7 @@ export type CreateBaseMutationResult< export type CreateMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > = CreateBaseMutationResult diff --git a/packages/svelte-query/src/createInfiniteQuery.ts b/packages/svelte-query/src/createInfiniteQuery.ts index c398805c7c..e78e5d9fe7 100644 --- a/packages/svelte-query/src/createInfiniteQuery.ts +++ b/packages/svelte-query/src/createInfiniteQuery.ts @@ -1,4 +1,9 @@ -import type { QueryObserver, QueryKey, QueryClient } from '@tanstack/query-core' +import type { + QueryObserver, + QueryKey, + QueryClient, + RegisteredError, +} from '@tanstack/query-core' import { InfiniteQueryObserver } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, @@ -8,7 +13,7 @@ import { createBaseQuery } from './createBaseQuery' export function createInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/svelte-query/src/createMutation.ts b/packages/svelte-query/src/createMutation.ts index 272737eb7b..736f5afa0b 100644 --- a/packages/svelte-query/src/createMutation.ts +++ b/packages/svelte-query/src/createMutation.ts @@ -1,5 +1,5 @@ import { readable, derived } from 'svelte/store' -import type { QueryClient } from '@tanstack/query-core' +import type { QueryClient, RegisteredError } from '@tanstack/query-core' import { MutationObserver, notifyManager } from '@tanstack/query-core' import type { CreateMutateFunction, @@ -10,7 +10,7 @@ import { useQueryClient } from './useQueryClient' export function createMutation< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, >( diff --git a/packages/svelte-query/src/createQueries.ts b/packages/svelte-query/src/createQueries.ts index 8dce735c0f..da168538e9 100644 --- a/packages/svelte-query/src/createQueries.ts +++ b/packages/svelte-query/src/createQueries.ts @@ -4,6 +4,7 @@ import type { QueryClient, QueriesPlaceholderDataFunction, QueryObserverResult, + RegisteredError, } from '@tanstack/query-core' import { notifyManager, QueriesObserver } from '@tanstack/query-core' @@ -16,7 +17,7 @@ import { useQueryClient } from './useQueryClient' // `placeholderData` function does not have a parameter type CreateQueryOptionsForCreateQueries< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = Omit< @@ -143,7 +144,7 @@ export type QueriesResults< ? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results QueryObserverResult< unknown extends TData ? TQueryFnData : TData, - unknown extends TError ? Error : TError + unknown extends TError ? RegisteredError : TError >[] : // Fallback QueryObserverResult[] diff --git a/packages/svelte-query/src/createQuery.ts b/packages/svelte-query/src/createQuery.ts index 3dd74bcf6e..f491e77f11 100644 --- a/packages/svelte-query/src/createQuery.ts +++ b/packages/svelte-query/src/createQuery.ts @@ -1,5 +1,9 @@ import { QueryObserver } from '@tanstack/query-core' -import type { QueryKey, QueryClient } from '@tanstack/query-core' +import type { + QueryKey, + QueryClient, + RegisteredError, +} from '@tanstack/query-core' import { createBaseQuery } from './createBaseQuery' import type { DefinedCreateQueryResult, @@ -9,7 +13,7 @@ import type { type UndefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = CreateQueryOptions & { @@ -18,7 +22,7 @@ type UndefinedInitialDataOptions< type DefinedInitialDataOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = CreateQueryOptions & { @@ -27,7 +31,7 @@ type DefinedInitialDataOptions< export function createQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -37,7 +41,7 @@ export function createQuery< export function createQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -47,7 +51,7 @@ export function createQuery< export function createQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/svelte-query/src/types.ts b/packages/svelte-query/src/types.ts index 844e3bc3e9..af81a5268a 100644 --- a/packages/svelte-query/src/types.ts +++ b/packages/svelte-query/src/types.ts @@ -8,6 +8,7 @@ import type { MutationObserverOptions, MutateFunction, DefinedQueryObserverResult, + RegisteredError, } from '@tanstack/query-core' import type { QueryClient } from '@tanstack/query-core' import type { Readable } from 'svelte/store' @@ -21,19 +22,21 @@ export interface ContextOptions { export interface CreateBaseQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends ContextOptions, QueryObserverOptions {} -export interface CreateBaseQueryResult - extends Readable> {} +export interface CreateBaseQueryResult< + TData = unknown, + TError = RegisteredError, +> extends Readable> {} export interface CreateQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends CreateBaseQueryOptions< @@ -44,12 +47,12 @@ export interface CreateQueryOptions< TQueryKey > {} -export interface CreateQueryResult +export interface CreateQueryResult extends CreateBaseQueryResult {} export interface CreateInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -63,22 +66,22 @@ export interface CreateInfiniteQueryOptions< export type CreateInfiniteQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = Readable> export type DefinedCreateBaseQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = Readable> export type DefinedCreateQueryResult< TData = unknown, - TError = Error, + TError = RegisteredError, > = DefinedCreateBaseQueryResult export interface CreateMutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > extends ContextOptions, @@ -89,7 +92,7 @@ export interface CreateMutationOptions< export type CreateMutateFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = ( @@ -98,14 +101,14 @@ export type CreateMutateFunction< export type CreateMutateAsyncFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = MutateFunction export type CreateBaseMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > = Override< @@ -117,7 +120,7 @@ export type CreateBaseMutationResult< export interface CreateMutationResult< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = unknown, TContext = unknown, > extends Readable< diff --git a/packages/vue-query/src/mutationCache.ts b/packages/vue-query/src/mutationCache.ts index 328526af53..1bd74aa755 100644 --- a/packages/vue-query/src/mutationCache.ts +++ b/packages/vue-query/src/mutationCache.ts @@ -1,10 +1,19 @@ import { MutationCache as MC } from '@tanstack/query-core' -import type { Mutation, MutationFilters } from '@tanstack/query-core' +import type { + Mutation, + MutationFilters, + RegisteredError, +} from '@tanstack/query-core' import type { MaybeRefDeep } from './types' import { cloneDeepUnref } from './utils' export class MutationCache extends MC { - find( + find< + TData = unknown, + TError = RegisteredError, + TVariables = any, + TContext = unknown, + >( filters: MaybeRefDeep, ): Mutation | undefined { return super.find(cloneDeepUnref(filters)) diff --git a/packages/vue-query/src/queryCache.ts b/packages/vue-query/src/queryCache.ts index 7ba2db7e4f..24b1b2c668 100644 --- a/packages/vue-query/src/queryCache.ts +++ b/packages/vue-query/src/queryCache.ts @@ -1,10 +1,15 @@ import { QueryCache as QC } from '@tanstack/query-core' -import type { Query, QueryFilters, WithRequired } from '@tanstack/query-core' +import type { + Query, + QueryFilters, + WithRequired, + RegisteredError, +} from '@tanstack/query-core' import type { MaybeRefDeep } from './types' import { cloneDeepUnref } from './utils' export class QueryCache extends QC { - find( + find( filters: MaybeRefDeep>, ): Query | undefined { return super.find(cloneDeepUnref(filters)) diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 144d1d4252..ef1030b096 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -22,6 +22,7 @@ import type { MutationFilters, QueryState, Updater, + RegisteredError, } from '@tanstack/query-core' import type { MaybeRefDeep } from './types' import { cloneDeepUnref } from './utils' @@ -85,7 +86,7 @@ export class QueryClient extends QC { ) } - getQueryState( + getQueryState( queryKey: MaybeRefDeep, ): QueryState | undefined { return super.getQueryState(cloneDeepUnref(queryKey)) @@ -131,7 +132,7 @@ export class QueryClient extends QC { fetchQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -139,7 +140,7 @@ export class QueryClient extends QC { ): Promise fetchQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -152,7 +153,7 @@ export class QueryClient extends QC { prefetchQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -160,7 +161,7 @@ export class QueryClient extends QC { ): Promise prefetchQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -173,7 +174,7 @@ export class QueryClient extends QC { fetchInfiniteQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -181,7 +182,7 @@ export class QueryClient extends QC { ): Promise> fetchInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -194,7 +195,7 @@ export class QueryClient extends QC { prefetchInfiniteQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -202,7 +203,7 @@ export class QueryClient extends QC { ): Promise prefetchInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index 42830036ed..1ffa0f100b 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -5,6 +5,7 @@ import type { QueryKey, InfiniteQueryObserverResult, InfiniteQueryObserverOptions, + RegisteredError, } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' @@ -16,7 +17,7 @@ import type { UnwrapRef } from 'vue-demi' export type UseInfiniteQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = unknown, TQueryData = unknown, TQueryKey extends QueryKey = QueryKey, @@ -68,7 +69,7 @@ export type UseInfiniteQueryReturnType = DistributiveOmit< export function useInfiniteQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( diff --git a/packages/vue-query/src/useMutation.ts b/packages/vue-query/src/useMutation.ts index 64ea5e5bfe..61c29529a9 100644 --- a/packages/vue-query/src/useMutation.ts +++ b/packages/vue-query/src/useMutation.ts @@ -12,6 +12,7 @@ import type { MutateFunction, MutationObserverResult, MutationObserverOptions, + RegisteredError, } from '@tanstack/query-core' import type { MaybeRefDeep, DistributiveOmit } from './types' import { MutationObserver } from '@tanstack/query-core' @@ -26,14 +27,14 @@ type MutationResult = DistributiveOmit< export type UseMutationOptions< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = MaybeRefDeep> type MutateSyncFunction< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, > = ( @@ -54,7 +55,7 @@ export type UseMutationReturnType< export function useMutation< TData = unknown, - TError = Error, + TError = RegisteredError, TVariables = void, TContext = unknown, >( diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index fd02a2e819..97dfd7bb84 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -6,6 +6,7 @@ import type { DefinedQueryObserverResult, WithRequired, QueryObserverOptions, + RegisteredError, } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' import type { UseBaseQueryReturnType } from './useBaseQuery' @@ -14,7 +15,7 @@ import type { QueryClient } from './queryClient' export type UseQueryOptions< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, @@ -64,7 +65,7 @@ export type UseQueryDefinedReturnType = DistributiveOmit< export function useQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -82,7 +83,7 @@ export function useQuery< export function useQuery< TQueryFnData = unknown, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( @@ -100,7 +101,7 @@ export function useQuery< export function useQuery< TQueryFnData, - TError = Error, + TError = RegisteredError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( From 5a156e22e0ea623f79fee3fff88e5a06d268d54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20M=C3=A1t=C3=A9=20Petr=C3=B3?= Date: Sun, 12 Feb 2023 14:54:51 +0100 Subject: [PATCH 034/314] chore(codemods): move existing codemods into a separate package (#4949) * chore(codemods): move existing codemods into a separate package * chore(codemods): fix formatting of the existing codemods * refactor(codemods): move existing utility functions to a shared place These utility functions can be reused for the version 5 codemods, so I decided to move them to a shared place. * refactor(codemods): move `UnprocessableKeyError` class to the `key-replacer.js` file Since it was used only here, it doesn't make any sense to keep it in a separate file. * chore(codemods): add `README.md` file under the `v5` directory --------- Co-authored-by: Dominik Dorfmeister --- .prettierignore | 1 + packages/codemods/.eslintrc | 15 ++++ packages/codemods/jest.config.ts | 4 + packages/codemods/package.json | 32 ++++++++ .../v4 => codemods/src}/utils/index.js | 24 +++--- .../transformers/query-cache-transformer.js | 43 +++++----- .../transformers/query-client-transformer.js | 15 ++-- .../use-query-like-transformer.js | 6 +- .../__testfixtures__/default-import.input.tsx | 0 .../default-import.output.tsx | 0 .../__testfixtures__/named-import.input.tsx | 0 .../__testfixtures__/named-import.output.tsx | 0 .../namespaced-import.input.tsx | 0 .../namespaced-import.output.tsx | 0 .../parameter-is-identifier.input.tsx | 0 .../parameter-is-identifier.output.tsx | 0 .../parameter-is-object-expression.input.tsx | 0 .../parameter-is-object-expression.output.tsx | 0 .../replace-import-specifier.input.tsx | 0 .../replace-import-specifier.output.tsx | 0 .../__testfixtures__/type-arguments.input.tsx | 0 .../type-arguments.output.tsx | 0 .../v4/__tests__/key-transformation.test.js | 2 +- .../replace-import-specifier.test.js | 0 .../src}/v4/key-transformation.js | 16 ++-- .../src}/v4/replace-import-specifier.js | 0 .../src}/v4/utils/replacers/key-replacer.js | 42 +++++----- packages/codemods/src/v5/README.md | 1 + packages/codemods/tsconfig.json | 10 +++ .../v4/utils/unprocessable-key-error.js | 8 -- packages/react-query/jest.config.ts | 2 +- packages/react-query/package.json | 8 +- pnpm-lock.yaml | 79 +++++++++---------- 33 files changed, 177 insertions(+), 131 deletions(-) create mode 100644 packages/codemods/.eslintrc create mode 100644 packages/codemods/jest.config.ts create mode 100644 packages/codemods/package.json rename packages/{react-query/codemods/v4 => codemods/src}/utils/index.js (90%) rename packages/{react-query/codemods/v4 => codemods/src}/utils/transformers/query-cache-transformer.js (77%) rename packages/{react-query/codemods/v4 => codemods/src}/utils/transformers/query-client-transformer.js (87%) rename packages/{react-query/codemods/v4 => codemods/src}/utils/transformers/use-query-like-transformer.js (94%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/default-import.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/default-import.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/named-import.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/named-import.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/namespaced-import.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/namespaced-import.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/parameter-is-identifier.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/parameter-is-identifier.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/parameter-is-object-expression.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/parameter-is-object-expression.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/replace-import-specifier.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/replace-import-specifier.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/type-arguments.input.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__testfixtures__/type-arguments.output.tsx (100%) rename packages/{react-query/codemods => codemods/src}/v4/__tests__/key-transformation.test.js (99%) rename packages/{react-query/codemods => codemods/src}/v4/__tests__/replace-import-specifier.test.js (100%) rename packages/{react-query/codemods => codemods/src}/v4/key-transformation.js (90%) rename packages/{react-query/codemods => codemods/src}/v4/replace-import-specifier.js (100%) rename packages/{react-query/codemods => codemods/src}/v4/utils/replacers/key-replacer.js (84%) create mode 100644 packages/codemods/src/v5/README.md create mode 100644 packages/codemods/tsconfig.json delete mode 100644 packages/react-query/codemods/v4/utils/unprocessable-key-error.js diff --git a/.prettierignore b/.prettierignore index 6da4f0816a..f36d862a39 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ /packages/svelte-query/.svelte-kit +/packages/codemods/**/__testfixtures__ diff --git a/packages/codemods/.eslintrc b/packages/codemods/.eslintrc new file mode 100644 index 0000000000..4f61a08475 --- /dev/null +++ b/packages/codemods/.eslintrc @@ -0,0 +1,15 @@ +{ + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module" + }, + "overrides": [ + { + "files": ["**/__testfixtures__/*"], + "rules": { + "import/no-unresolved": "off", + "@typescript-eslint/no-unused-vars": "off" + } + } + ] +} diff --git a/packages/codemods/jest.config.ts b/packages/codemods/jest.config.ts new file mode 100644 index 0000000000..4af30741b6 --- /dev/null +++ b/packages/codemods/jest.config.ts @@ -0,0 +1,4 @@ +export default { + displayName: 'query-codemods', + preset: '../../jest-preset.js' +} diff --git a/packages/codemods/package.json b/packages/codemods/package.json new file mode 100644 index 0000000000..28b6d5df84 --- /dev/null +++ b/packages/codemods/package.json @@ -0,0 +1,32 @@ +{ + "name": "@tanstack/query-codemods", + "version": "4.24.3", + "description": "Collection of codemods to make the migration easier.", + "author": "Balázs Máté Petró", + "license": "MIT", + "repository": "tanstack/query", + "homepage": "https://tanstack.com/query", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "exports": { + "./package.json": "./package.json" + }, + "sideEffects": false, + "scripts": { + "test:eslint": "eslint --ext .ts,.tsx ./src", + "test:lib": "jest --config ./jest.config.ts", + "test:lib:dev": "pnpm run test:lib --watch" + }, + "files": [ + "src", + "!src/jest.config.js", + "!src/**/__testfixtures__", + "!src/**/__tests__" + ], + "devDependencies": { + "@types/jscodeshift": "0.11.5", + "jscodeshift": "0.13.1" + } +} diff --git a/packages/react-query/codemods/v4/utils/index.js b/packages/codemods/src/utils/index.js similarity index 90% rename from packages/react-query/codemods/v4/utils/index.js rename to packages/codemods/src/utils/index.js index 4706ade268..152ce45ebe 100644 --- a/packages/react-query/codemods/v4/utils/index.js +++ b/packages/codemods/src/utils/index.js @@ -1,7 +1,7 @@ module.exports = ({ root, jscodeshift }) => { const findImportIdentifierOf = (importSpecifiers, identifier) => { const specifier = importSpecifiers - .filter(node => node.value.imported.name === identifier) + .filter((node) => node.value.imported.name === identifier) .paths() if (specifier.length > 0) { @@ -20,7 +20,7 @@ module.exports = ({ root, jscodeshift }) => { }) .find(jscodeshift.ImportSpecifier, {}) - const locateImports = identifiers => { + const locateImports = (identifiers) => { const findNamespaceImportIdentifier = () => { const specifier = root .find(jscodeshift.ImportDeclaration, { @@ -59,7 +59,7 @@ module.exports = ({ root, jscodeshift }) => { for (const identifier of identifiers) { identifierMap[identifier] = findImportIdentifierOf( importSpecifiers, - identifier + identifier, ) } @@ -81,21 +81,21 @@ module.exports = ({ root, jscodeshift }) => { }, }) - const findQueryClientIdentifiers = importIdentifiers => + const findQueryClientIdentifiers = (importIdentifiers) => root .find(jscodeshift.VariableDeclarator, {}) - .filter(node => { + .filter((node) => { if (node.value.init) { const initializer = node.value.init return ( isClassInstantiationOf( initializer, - getSelectorByImports(importIdentifiers, 'QueryClient') + getSelectorByImports(importIdentifiers, 'QueryClient'), ) || isFunctionCallOf( initializer, - getSelectorByImports(importIdentifiers, 'useQueryClient') + getSelectorByImports(importIdentifiers, 'useQueryClient'), ) ) } @@ -103,18 +103,18 @@ module.exports = ({ root, jscodeshift }) => { return false }) .paths() - .map(node => node.value.id.name) + .map((node) => node.value.id.name) - const isCallExpression = node => + const isCallExpression = (node) => jscodeshift.match(node, { type: jscodeshift.CallExpression.name }) - const isIdentifier = node => + const isIdentifier = (node) => jscodeshift.match(node, { type: jscodeshift.Identifier.name }) - const isMemberExpression = node => + const isMemberExpression = (node) => jscodeshift.match(node, { type: jscodeshift.MemberExpression.name }) - const isNewExpression = node => + const isNewExpression = (node) => jscodeshift.match(node, { type: jscodeshift.NewExpression.name }) const isClassInstantiationOf = (node, selector) => { diff --git a/packages/react-query/codemods/v4/utils/transformers/query-cache-transformer.js b/packages/codemods/src/utils/transformers/query-cache-transformer.js similarity index 77% rename from packages/react-query/codemods/v4/utils/transformers/query-cache-transformer.js rename to packages/codemods/src/utils/transformers/query-cache-transformer.js index acdea9550c..873794f11d 100644 --- a/packages/react-query/codemods/v4/utils/transformers/query-cache-transformer.js +++ b/packages/codemods/src/utils/transformers/query-cache-transformer.js @@ -2,15 +2,15 @@ module.exports = ({ jscodeshift, utils, root }) => { const isGetQueryCacheMethodCall = ( initializer, importIdentifiers, - knownQueryClientIds + knownQueryClientIds, ) => { - const isKnownQueryClient = node => + const isKnownQueryClient = (node) => utils.isIdentifier(node) && knownQueryClientIds.includes(node.name) - const isGetQueryCacheIdentifier = node => + const isGetQueryCacheIdentifier = (node) => utils.isIdentifier(node) && node.name === 'getQueryCache' - const isValidInitializer = node => + const isValidInitializer = (node) => utils.isCallExpression(node) && utils.isMemberExpression(node.callee) if (isValidInitializer(initializer)) { @@ -21,7 +21,7 @@ module.exports = ({ jscodeshift, utils, root }) => { (isKnownQueryClient(instance) || utils.isFunctionCallOf( instance, - utils.getSelectorByImports(importIdentifiers, 'useQueryClient') + utils.getSelectorByImports(importIdentifiers, 'useQueryClient'), )) ) } @@ -31,21 +31,21 @@ module.exports = ({ jscodeshift, utils, root }) => { const findQueryCacheInstantiations = ( importIdentifiers, - knownQueryClientIds + knownQueryClientIds, ) => - root.find(jscodeshift.VariableDeclarator, {}).filter(node => { + root.find(jscodeshift.VariableDeclarator, {}).filter((node) => { if (node.value.init) { const initializer = node.value.init return ( utils.isClassInstantiationOf( initializer, - utils.getSelectorByImports(importIdentifiers, 'QueryCache') + utils.getSelectorByImports(importIdentifiers, 'QueryCache'), ) || isGetQueryCacheMethodCall( initializer, importIdentifiers, - knownQueryClientIds + knownQueryClientIds, ) ) } @@ -53,35 +53,36 @@ module.exports = ({ jscodeshift, utils, root }) => { return false }) - const filterQueryCacheMethodCalls = node => + const filterQueryCacheMethodCalls = (node) => utils.isIdentifier(node) && ['find', 'findAll'].includes(node.name) - const findQueryCacheMethodCalls = importIdentifiers => { + const findQueryCacheMethodCalls = (importIdentifiers) => { /** * Here we collect all query client instantiations. We have to make aware of them because the query cache can be * accessed by the query client as well. */ - const queryClientIdentifiers = utils.queryClient.findQueryClientIdentifiers( - importIdentifiers - ) + const queryClientIdentifiers = + utils.queryClient.findQueryClientIdentifiers(importIdentifiers) /** * Here we collect all query cache instantiations. The reason is simple: the methods can be called on query cache * instances, to locate the possible usages we need to be aware of the identifier names. */ const queryCacheIdentifiers = findQueryCacheInstantiations( importIdentifiers, - queryClientIdentifiers + queryClientIdentifiers, ) .paths() - .map(node => node.value.id.name) + .map((node) => node.value.id.name) return ( utils // First, we need to find all method calls. .findAllMethodCalls() // Then we narrow the collection to all `fetch` and `fetchAll` methods. - .filter(node => filterQueryCacheMethodCalls(node.value.callee.property)) - .filter(node => { + .filter((node) => + filterQueryCacheMethodCalls(node.value.callee.property), + ) + .filter((node) => { const object = node.value.callee.object // If the method is called on a `QueryCache` instance, we keep it in the collection. @@ -94,7 +95,7 @@ module.exports = ({ jscodeshift, utils, root }) => { return isGetQueryCacheMethodCall( object, importIdentifiers, - queryClientIdentifiers + queryClientIdentifiers, ) } @@ -103,9 +104,9 @@ module.exports = ({ jscodeshift, utils, root }) => { ) } - const execute = replacer => { + const execute = (replacer) => { findQueryCacheMethodCalls( - utils.locateImports(['QueryCache', 'QueryClient', 'useQueryClient']) + utils.locateImports(['QueryCache', 'QueryClient', 'useQueryClient']), ).replaceWith(replacer) } diff --git a/packages/react-query/codemods/v4/utils/transformers/query-client-transformer.js b/packages/codemods/src/utils/transformers/query-client-transformer.js similarity index 87% rename from packages/react-query/codemods/v4/utils/transformers/query-client-transformer.js rename to packages/codemods/src/utils/transformers/query-client-transformer.js index a1fd70f232..ee904ba287 100644 --- a/packages/react-query/codemods/v4/utils/transformers/query-client-transformer.js +++ b/packages/codemods/src/utils/transformers/query-client-transformer.js @@ -7,19 +7,18 @@ module.exports = ({ jscodeshift, utils, root }) => { * Here we collect all query client instantiations. We have to make aware of them because some method calls might * be invoked on these instances. */ - const queryClientIdentifiers = utils.queryClient.findQueryClientIdentifiers( - importIdentifiers - ) + const queryClientIdentifiers = + utils.queryClient.findQueryClientIdentifiers(importIdentifiers) return ( utils // First, we need to find all method calls. .findAllMethodCalls() // Then we narrow the collection to `QueryClient` methods. - .filter(node => - filterQueryClientMethodCalls(node.value.callee.property, methods) + .filter((node) => + filterQueryClientMethodCalls(node.value.callee.property, methods), ) - .filter(node => { + .filter((node) => { const object = node.value.callee.object // If the method is called on a `QueryClient` instance, we keep it in the collection. @@ -30,7 +29,7 @@ module.exports = ({ jscodeshift, utils, root }) => { // If the method is called on the return value of `useQueryClient` hook, we keep it in the collection. return utils.isFunctionCallOf( object, - utils.getSelectorByImports(importIdentifiers, 'useQueryClient') + utils.getSelectorByImports(importIdentifiers, 'useQueryClient'), ) }) ) @@ -39,7 +38,7 @@ module.exports = ({ jscodeshift, utils, root }) => { const execute = (methods, replacer) => { findQueryClientMethodCalls( utils.locateImports(['QueryClient', 'useQueryClient']), - methods + methods, ).replaceWith(replacer) } diff --git a/packages/react-query/codemods/v4/utils/transformers/use-query-like-transformer.js b/packages/codemods/src/utils/transformers/use-query-like-transformer.js similarity index 94% rename from packages/react-query/codemods/v4/utils/transformers/use-query-like-transformer.js rename to packages/codemods/src/utils/transformers/use-query-like-transformer.js index da3a6d527d..fa6b65682c 100644 --- a/packages/react-query/codemods/v4/utils/transformers/use-query-like-transformer.js +++ b/packages/codemods/src/utils/transformers/use-query-like-transformer.js @@ -16,13 +16,13 @@ module.exports = ({ jscodeshift, utils, root }) => { // First, we need to find all call expressions. .find(jscodeshift.CallExpression, {}) // Then we narrow the collection to the `useQuery` like hook calls. - .filter(node => - filterUseQueryLikeHookCalls(node.value, importIdentifiers, hooks) + .filter((node) => + filterUseQueryLikeHookCalls(node.value, importIdentifiers, hooks), ) const execute = (hooks, replacer) => { findUseQueryLikeHookCalls(utils.locateImports(hooks), hooks).replaceWith( - replacer + replacer, ) } diff --git a/packages/react-query/codemods/v4/__testfixtures__/default-import.input.tsx b/packages/codemods/src/v4/__testfixtures__/default-import.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/default-import.input.tsx rename to packages/codemods/src/v4/__testfixtures__/default-import.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/default-import.output.tsx b/packages/codemods/src/v4/__testfixtures__/default-import.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/default-import.output.tsx rename to packages/codemods/src/v4/__testfixtures__/default-import.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/named-import.input.tsx b/packages/codemods/src/v4/__testfixtures__/named-import.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/named-import.input.tsx rename to packages/codemods/src/v4/__testfixtures__/named-import.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/named-import.output.tsx b/packages/codemods/src/v4/__testfixtures__/named-import.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/named-import.output.tsx rename to packages/codemods/src/v4/__testfixtures__/named-import.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/namespaced-import.input.tsx b/packages/codemods/src/v4/__testfixtures__/namespaced-import.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/namespaced-import.input.tsx rename to packages/codemods/src/v4/__testfixtures__/namespaced-import.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/namespaced-import.output.tsx b/packages/codemods/src/v4/__testfixtures__/namespaced-import.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/namespaced-import.output.tsx rename to packages/codemods/src/v4/__testfixtures__/namespaced-import.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx b/packages/codemods/src/v4/__testfixtures__/parameter-is-identifier.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx rename to packages/codemods/src/v4/__testfixtures__/parameter-is-identifier.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx b/packages/codemods/src/v4/__testfixtures__/parameter-is-identifier.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx rename to packages/codemods/src/v4/__testfixtures__/parameter-is-identifier.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx b/packages/codemods/src/v4/__testfixtures__/parameter-is-object-expression.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx rename to packages/codemods/src/v4/__testfixtures__/parameter-is-object-expression.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx b/packages/codemods/src/v4/__testfixtures__/parameter-is-object-expression.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx rename to packages/codemods/src/v4/__testfixtures__/parameter-is-object-expression.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/replace-import-specifier.input.tsx b/packages/codemods/src/v4/__testfixtures__/replace-import-specifier.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/replace-import-specifier.input.tsx rename to packages/codemods/src/v4/__testfixtures__/replace-import-specifier.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/replace-import-specifier.output.tsx b/packages/codemods/src/v4/__testfixtures__/replace-import-specifier.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/replace-import-specifier.output.tsx rename to packages/codemods/src/v4/__testfixtures__/replace-import-specifier.output.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/type-arguments.input.tsx b/packages/codemods/src/v4/__testfixtures__/type-arguments.input.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/type-arguments.input.tsx rename to packages/codemods/src/v4/__testfixtures__/type-arguments.input.tsx diff --git a/packages/react-query/codemods/v4/__testfixtures__/type-arguments.output.tsx b/packages/codemods/src/v4/__testfixtures__/type-arguments.output.tsx similarity index 100% rename from packages/react-query/codemods/v4/__testfixtures__/type-arguments.output.tsx rename to packages/codemods/src/v4/__testfixtures__/type-arguments.output.tsx diff --git a/packages/react-query/codemods/v4/__tests__/key-transformation.test.js b/packages/codemods/src/v4/__tests__/key-transformation.test.js similarity index 99% rename from packages/react-query/codemods/v4/__tests__/key-transformation.test.js rename to packages/codemods/src/v4/__tests__/key-transformation.test.js index b22431aec4..99a06d5e67 100644 --- a/packages/react-query/codemods/v4/__tests__/key-transformation.test.js +++ b/packages/codemods/src/v4/__tests__/key-transformation.test.js @@ -26,7 +26,7 @@ defineTest( 'parameter-is-object-expression', { parser: 'tsx', - } + }, ) defineTest(__dirname, 'key-transformation', null, 'type-arguments', { diff --git a/packages/react-query/codemods/v4/__tests__/replace-import-specifier.test.js b/packages/codemods/src/v4/__tests__/replace-import-specifier.test.js similarity index 100% rename from packages/react-query/codemods/v4/__tests__/replace-import-specifier.test.js rename to packages/codemods/src/v4/__tests__/replace-import-specifier.test.js diff --git a/packages/react-query/codemods/v4/key-transformation.js b/packages/codemods/src/v4/key-transformation.js similarity index 90% rename from packages/react-query/codemods/v4/key-transformation.js rename to packages/codemods/src/v4/key-transformation.js index 8c6b1bc1b5..3ebb2dfc07 100644 --- a/packages/react-query/codemods/v4/key-transformation.js +++ b/packages/codemods/src/v4/key-transformation.js @@ -1,13 +1,13 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires -const createUtilsObject = require('./utils') +const createUtilsObject = require('../utils') // eslint-disable-next-line @typescript-eslint/no-var-requires const createKeyReplacer = require('./utils/replacers/key-replacer') // eslint-disable-next-line @typescript-eslint/no-var-requires -const createUseQueryLikeTransformer = require('./utils/transformers/use-query-like-transformer') +const createUseQueryLikeTransformer = require('../utils/transformers/use-query-like-transformer') // eslint-disable-next-line @typescript-eslint/no-var-requires -const createQueryClientTransformer = require('./utils/transformers/query-client-transformer') +const createQueryClientTransformer = require('../utils/transformers/query-client-transformer') // eslint-disable-next-line @typescript-eslint/no-var-requires -const createQueryCacheTransformer = require('./utils/transformers/query-cache-transformer') +const createQueryCacheTransformer = require('../utils/transformers/query-cache-transformer') const transformQueryClientUsages = ({ jscodeshift, utils, root, filePath }) => { const transformer = createQueryClientTransformer({ jscodeshift, utils, root }) @@ -37,7 +37,7 @@ const transformQueryClientUsages = ({ jscodeshift, utils, root, filePath }) => { 'removeQueries', 'resetQueries', ], - replacer + replacer, ) } @@ -61,7 +61,7 @@ const transformUseQueriesUsages = ({ jscodeshift, utils, root }) => { jscodeshift.property( 'init', jscodeshift.identifier('queries'), - node.original.arguments[0] + node.original.arguments[0], ), ]), ]) @@ -96,7 +96,7 @@ const transformUseQueryLikeUsages = ({ root, filePath, keyName: 'queryKey', - }) + }), ) transformer.execute( ['useMutation'], @@ -105,7 +105,7 @@ const transformUseQueryLikeUsages = ({ root, filePath, keyName: 'mutationKey', - }) + }), ) } diff --git a/packages/react-query/codemods/v4/replace-import-specifier.js b/packages/codemods/src/v4/replace-import-specifier.js similarity index 100% rename from packages/react-query/codemods/v4/replace-import-specifier.js rename to packages/codemods/src/v4/replace-import-specifier.js diff --git a/packages/react-query/codemods/v4/utils/replacers/key-replacer.js b/packages/codemods/src/v4/utils/replacers/key-replacer.js similarity index 84% rename from packages/react-query/codemods/v4/utils/replacers/key-replacer.js rename to packages/codemods/src/v4/utils/replacers/key-replacer.js index 621902edb5..1bd62984e6 100644 --- a/packages/react-query/codemods/v4/utils/replacers/key-replacer.js +++ b/packages/codemods/src/v4/utils/replacers/key-replacer.js @@ -1,18 +1,22 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires -const UnprocessableKeyError = require('../unprocessable-key-error') +class UnprocessableKeyError extends Error { + constructor(message) { + super(message) + this.name = 'UnprocessableKeyError' + } +} module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { - const isArrayExpression = node => + const isArrayExpression = (node) => jscodeshift.match(node, { type: jscodeshift.ArrayExpression.name }) - const isStringLiteral = node => + const isStringLiteral = (node) => jscodeshift.match(node, { type: jscodeshift.StringLiteral.name }) || jscodeshift.match(node, { type: jscodeshift.Literal.name }) - const isTemplateLiteral = node => + const isTemplateLiteral = (node) => jscodeshift.match(node, { type: jscodeshift.TemplateLiteral.name }) - const findVariableDeclaration = node => { + const findVariableDeclaration = (node) => { const declarations = root .find(jscodeshift.VariableDeclarator, { id: { @@ -25,7 +29,7 @@ module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { return declarations.length > 0 ? declarations[0] : null } - const createKeyValue = node => { + const createKeyValue = (node) => { // When the node is a string literal we convert it into an array of strings. if (isStringLiteral(node)) { return jscodeshift.arrayExpression([ @@ -47,7 +51,7 @@ module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { if (!variableDeclaration) { throw new UnprocessableKeyError( - `In file ${filePath} at line ${node.loc.start.line} the type of identifier \`${node.name}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.` + `In file ${filePath} at line ${node.loc.start.line} the type of identifier \`${node.name}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.`, ) } @@ -60,20 +64,20 @@ module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { } throw new UnprocessableKeyError( - `In file ${filePath} at line ${node.loc.start.line} the type of the \`${keyName}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.` + `In file ${filePath} at line ${node.loc.start.line} the type of the \`${keyName}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.`, ) } - const createKeyProperty = node => + const createKeyProperty = (node) => jscodeshift.property( 'init', jscodeshift.identifier(keyName), - createKeyValue(node) + createKeyValue(node), ) const getPropertyFromObjectExpression = (objectExpression, propertyName) => objectExpression.properties.find( - property => property.key.name === propertyName + (property) => property.key.name === propertyName, ) ?? null const buildWithTypeArguments = (node, builder) => { @@ -110,27 +114,27 @@ module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { ) { const originalKey = getPropertyFromObjectExpression( firstArgument, - keyName + keyName, ) if (!originalKey) { throw new UnprocessableKeyError( - `In file ${filePath} at line ${node.loc.start.line} the \`${keyName}\` couldn't be found. Did you forget to add it?` + `In file ${filePath} at line ${node.loc.start.line} the \`${keyName}\` couldn't be found. Did you forget to add it?`, ) } const restOfTheProperties = firstArgument.properties.filter( - item => item.key.name !== keyName + (item) => item.key.name !== keyName, ) - return buildWithTypeArguments(node, originalNode => + return buildWithTypeArguments(node, (originalNode) => jscodeshift.callExpression(originalNode.original.callee, [ jscodeshift.objectExpression([ createKeyProperty(originalKey.value), ...restOfTheProperties, ]), ...restOfTheArguments, - ]) + ]), ) } @@ -139,11 +143,11 @@ module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { return node } - return buildWithTypeArguments(node, originalNode => + return buildWithTypeArguments(node, (originalNode) => jscodeshift.callExpression(originalNode.original.callee, [ createKeyValue(firstArgument), ...restOfTheArguments, - ]) + ]), ) } catch (error) { if (error.name === 'UnprocessableKeyError') { diff --git a/packages/codemods/src/v5/README.md b/packages/codemods/src/v5/README.md new file mode 100644 index 0000000000..5941d81a32 --- /dev/null +++ b/packages/codemods/src/v5/README.md @@ -0,0 +1 @@ +# Codemods for v5 diff --git a/packages/codemods/tsconfig.json b/packages/codemods/tsconfig.json new file mode 100644 index 0000000000..9ee9f26e25 --- /dev/null +++ b/packages/codemods/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./build/lib", + "tsBuildInfoFile": "./build/.tsbuildinfo" + }, + "include": ["src"] +} diff --git a/packages/react-query/codemods/v4/utils/unprocessable-key-error.js b/packages/react-query/codemods/v4/utils/unprocessable-key-error.js deleted file mode 100644 index 87935c584b..0000000000 --- a/packages/react-query/codemods/v4/utils/unprocessable-key-error.js +++ /dev/null @@ -1,8 +0,0 @@ -class UnprocessableKeyError extends Error { - constructor(message) { - super(message) - this.name = 'UnprocessableKeyError' - } -} - -module.exports = UnprocessableKeyError diff --git a/packages/react-query/jest.config.ts b/packages/react-query/jest.config.ts index f4a5b3fedd..38e6f708ab 100644 --- a/packages/react-query/jest.config.ts +++ b/packages/react-query/jest.config.ts @@ -2,5 +2,5 @@ export default { displayName: 'react-query', preset: '../../jest-preset.js', setupFilesAfterEnv: ['./jest.setup.ts'], - testMatch: ['/src/**/*.test.tsx', '/codemods/**/*.test.js'], + testMatch: ['/src/**/*.test.tsx'], } diff --git a/packages/react-query/package.json b/packages/react-query/package.json index c5f861680e..b91d8e4fe4 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -33,17 +33,11 @@ "files": [ "build/lib/*", "build/umd/*", - "src", - "codemods", - "!codemods/jest.config.js", - "!codemods/**/__testfixtures__", - "!codemods/**/__tests__" + "src" ], "devDependencies": { - "@types/jscodeshift": "^0.11.3", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", - "jscodeshift": "^0.13.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^3.1.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3958517431..615b973285 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -438,7 +438,7 @@ importers: expo-status-bar: 1.1.0 react: 17.0.1 react-dom: 17.0.1_react@17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 react-native-gesture-handler: 1.10.3 react-native-paper: 4.9.2_sbjh7r6wrxe2pvsvaqturwwxna react-native-reanimated: 2.2.4_euatrkfywk4k5qvuxs5sao62oq @@ -805,7 +805,7 @@ importers: postcss: 8.4.21 svelte: 3.55.0 svelte-check: 2.10.3_77wbasr76lhjripnylrva3hecy - tailwindcss: 3.2.4_postcss@8.4.21 + tailwindcss: 3.2.4 tslib: 2.4.1 typescript: 4.8.4 vite: 4.0.4 @@ -859,6 +859,14 @@ importers: typescript: 4.8.4 vite: 3.2.2 + packages/codemods: + specifiers: + '@types/jscodeshift': 0.11.5 + jscodeshift: 0.13.1 + devDependencies: + '@types/jscodeshift': 0.11.5 + jscodeshift: 0.13.1 + packages/eslint-plugin-query: specifiers: '@typescript-eslint/eslint-plugin': ^5.41.0 @@ -905,20 +913,16 @@ importers: packages/react-query: specifiers: '@tanstack/query-core': workspace:* - '@types/jscodeshift': ^0.11.3 '@types/react': ^18.0.14 '@types/react-dom': ^18.0.5 - jscodeshift: ^0.13.1 react: ^18.2.0 react-dom: ^18.2.0 react-error-boundary: ^3.1.4 dependencies: '@tanstack/query-core': link:../query-core devDependencies: - '@types/jscodeshift': 0.11.5 '@types/react': 18.0.15 '@types/react-dom': 18.0.6 - jscodeshift: 0.13.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-error-boundary: 3.1.4_react@18.2.0 @@ -4585,15 +4589,14 @@ packages: write-file-atomic: 2.4.3 dev: false - /@expo/metro-config/0.1.84_@babel+core@7.19.1: + /@expo/metro-config/0.1.84: resolution: {integrity: sha512-xWSfM0+AxcKw0H8mc1RuKs4Yy4JT4SJfn4yDnGLAlKkHlEC+D2seZvb/Tdd173e/LANmcarNd+OcDYu03AmVWA==} dependencies: '@expo/config': 5.0.9 chalk: 4.1.2 getenv: 1.0.0 - metro-react-native-babel-transformer: 0.59.0_@babel+core@7.19.1 + metro-react-native-babel-transformer: 0.59.0 transitivePeerDependencies: - - '@babel/core' - supports-color dev: false @@ -5699,7 +5702,7 @@ packages: ora: 3.4.0 dev: false - /@react-native-community/cli/5.0.1_wkcrzi46eu62irrbbalfhjeu4u: + /@react-native-community/cli/5.0.1_react-native@0.64.3: resolution: {integrity: sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw==} engines: {node: '>=12'} hasBin: true @@ -5729,7 +5732,7 @@ packages: metro: 0.64.0 metro-config: 0.64.0 metro-core: 0.64.0 - metro-react-native-babel-transformer: 0.64.0_@babel+core@7.19.1 + metro-react-native-babel-transformer: 0.64.0 metro-resolver: 0.64.0 metro-runtime: 0.64.0 minimist: 1.2.6 @@ -5738,14 +5741,13 @@ packages: ora: 3.4.0 pretty-format: 26.6.2 prompts: 2.4.2 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 semver: 6.3.0 serve-static: 1.15.0 strip-ansi: 5.2.0 sudo-prompt: 9.2.1 wcwidth: 1.0.1 transitivePeerDependencies: - - '@babel/core' - bufferutil - encoding - supports-color @@ -5757,7 +5759,7 @@ packages: peerDependencies: react-native: '>=0.59' dependencies: - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 dev: false /@react-native/assets/1.0.0: @@ -5799,7 +5801,7 @@ packages: dependencies: '@react-navigation/native': 6.0.11_sbjh7r6wrxe2pvsvaqturwwxna react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 react-native-safe-area-context: 3.3.2_sbjh7r6wrxe2pvsvaqturwwxna dev: false @@ -5814,7 +5816,7 @@ packages: fast-deep-equal: 3.1.3 nanoid: 3.3.4 react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 dev: false /@react-navigation/routers/6.1.1: @@ -5837,7 +5839,7 @@ packages: '@react-navigation/native': 6.0.11_sbjh7r6wrxe2pvsvaqturwwxna color: 4.2.3 react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 react-native-gesture-handler: 1.10.3 react-native-safe-area-context: 3.3.2_sbjh7r6wrxe2pvsvaqturwwxna react-native-screens: 3.8.0_sbjh7r6wrxe2pvsvaqturwwxna @@ -6124,7 +6126,7 @@ packages: '@tanstack/query-core': 4.24.4 react: 17.0.1 react-dom: 17.0.1_react@17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 use-sync-external-store: 1.2.0_react@17.0.1 dev: false @@ -8029,7 +8031,7 @@ packages: '@babel/preset-env': 7.18.6_@babel+core@7.19.1 babel-plugin-module-resolver: 4.1.0 babel-plugin-react-native-web: 0.17.7 - metro-react-native-babel-preset: 0.64.0_@babel+core@7.19.1 + metro-react-native-babel-preset: 0.64.0 transitivePeerDependencies: - '@babel/core' - supports-color @@ -10505,7 +10507,7 @@ packages: hasBin: true dependencies: '@babel/runtime': 7.18.9 - '@expo/metro-config': 0.1.84_@babel+core@7.19.1 + '@expo/metro-config': 0.1.84 '@expo/vector-icons': 12.0.5 babel-preset-expo: 8.5.1_@babel+core@7.19.1 cross-spawn: 6.0.5 @@ -13291,10 +13293,8 @@ packages: - supports-color dev: false - /metro-react-native-babel-preset/0.64.0_@babel+core@7.19.1: + /metro-react-native-babel-preset/0.64.0: resolution: {integrity: sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ==} - peerDependencies: - '@babel/core': '*' dependencies: '@babel/core': 7.19.1 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.19.1 @@ -13339,10 +13339,8 @@ packages: - supports-color dev: false - /metro-react-native-babel-transformer/0.59.0_@babel+core@7.19.1: + /metro-react-native-babel-transformer/0.59.0: resolution: {integrity: sha512-1O3wrnMq4NcPQ1asEcl9lRDn/t+F1Oef6S9WaYVIKEhg9m/EQRGVrrTVP+R6B5Eeaj3+zNKbzM8Dx/NWy1hUbQ==} - peerDependencies: - '@babel/core': '*' dependencies: '@babel/core': 7.19.1 babel-preset-fbjs: 3.4.0_@babel+core@7.19.1 @@ -13353,15 +13351,13 @@ packages: - supports-color dev: false - /metro-react-native-babel-transformer/0.64.0_@babel+core@7.19.1: + /metro-react-native-babel-transformer/0.64.0: resolution: {integrity: sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w==} - peerDependencies: - '@babel/core': '*' dependencies: '@babel/core': 7.19.1 babel-preset-fbjs: 3.4.0_@babel+core@7.19.1 metro-babel-transformer: 0.64.0 - metro-react-native-babel-preset: 0.64.0_@babel+core@7.19.1 + metro-react-native-babel-preset: 0.64.0 metro-source-map: 0.64.0 nullthrows: 1.1.1 transitivePeerDependencies: @@ -13507,7 +13503,7 @@ packages: metro-hermes-compiler: 0.64.0 metro-inspector-proxy: 0.64.0 metro-minify-uglify: 0.64.0 - metro-react-native-babel-preset: 0.64.0_@babel+core@7.19.1 + metro-react-native-babel-preset: 0.64.0 metro-resolver: 0.64.0 metro-runtime: 0.64.0 metro-source-map: 0.64.0 @@ -14840,7 +14836,7 @@ packages: peerDependencies: react-native: '>=0.42.0' dependencies: - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 dev: false /react-native-paper/4.9.2_sbjh7r6wrxe2pvsvaqturwwxna: @@ -14853,7 +14849,7 @@ packages: '@callstack/react-theme-provider': 3.0.7_react@17.0.1 color: 3.2.1 react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 react-native-iphone-x-helper: 1.3.1_react-native@0.64.3 dev: false @@ -14868,7 +14864,7 @@ packages: fbjs: 3.0.4 mockdate: 3.0.5 react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 react-native-gesture-handler: 1.10.3 string-hash-64: 1.0.3 transitivePeerDependencies: @@ -14883,7 +14879,7 @@ packages: react-native: '*' dependencies: react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 dev: false /react-native-screens/3.8.0_sbjh7r6wrxe2pvsvaqturwwxna: @@ -14893,7 +14889,7 @@ packages: react-native: '*' dependencies: react: 17.0.1 - react-native: 0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm + react-native: 0.64.3_react@17.0.1 warn-once: 0.1.0 dev: false @@ -14916,7 +14912,7 @@ packages: - encoding dev: false - /react-native/0.64.3_gpe6tsd6gj6gjuc7hywxyjd2qm: + /react-native/0.64.3_react@17.0.1: resolution: {integrity: sha512-2OEU74U0Ek1/WeBzPbg6XDsCfjF/9fhrNX/5TFgEiBKd5mNc9LOZ/OlMmkb7iues/ZZ/oc51SbEfLRQdcW0fVw==} engines: {node: '>=12'} hasBin: true @@ -14924,7 +14920,7 @@ packages: react: 17.0.1 dependencies: '@jest/create-cache-key-function': 26.6.2 - '@react-native-community/cli': 5.0.1_wkcrzi46eu62irrbbalfhjeu4u + '@react-native-community/cli': 5.0.1_react-native@0.64.3 '@react-native-community/cli-platform-android': 5.0.1 '@react-native-community/cli-platform-ios': 5.0.2 '@react-native/assets': 1.0.0 @@ -14938,7 +14934,7 @@ packages: invariant: 2.2.4 jsc-android: 245459.0.0 metro-babel-register: 0.64.0 - metro-react-native-babel-transformer: 0.64.0_@babel+core@7.19.1 + metro-react-native-babel-transformer: 0.64.0 metro-runtime: 0.64.0 metro-source-map: 0.64.0 nullthrows: 1.1.1 @@ -14957,7 +14953,6 @@ packages: whatwg-fetch: 3.0.0 ws: 6.2.2 transitivePeerDependencies: - - '@babel/core' - '@babel/preset-env' - bufferutil - encoding @@ -16396,12 +16391,10 @@ packages: strip-ansi: 6.0.1 dev: true - /tailwindcss/3.2.4_postcss@8.4.21: + /tailwindcss/3.2.4: resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==} engines: {node: '>=12.13.0'} hasBin: true - peerDependencies: - postcss: ^8.0.9 dependencies: arg: 5.0.2 chokidar: 3.5.3 From 35055d752753ed14effbdd838ebb7c0c9a983c55 Mon Sep 17 00:00:00 2001 From: Prateek Surana Date: Mon, 13 Feb 2023 02:03:08 +0530 Subject: [PATCH 035/314] feat: Update isLoading behaviour (#4888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :recycle: Add isPending and update isLoading behaviour * ✅ Fix useQuery tests * ✅ Fix query core tests * ✅ Fix react-query tests * :recycle: Update mutation to use isPending * :recycle: Mark isInitialLoading as deprecated * ✅ Fix query core mutation tests * ✅ Fix persist query tests * ✅ Fix solid query tests * ✅ Fix all vue query tests * :memo: isLoading -> isPending wherever necessary * :memo: Use `pending` for `loading` and remove references for `isInitialLoading` * :memo: Use `pending` for `loading` and remove references for `isInitialLoading` * :memo: Update upgrade guide * :ok_hand: Review updates * :sparkles: Updated examples to use isPending * Update docs/react/reference/useQuery.md --------- Co-authored-by: Dominik Dorfmeister --- docs/react/adapters/react-query.md | 2 +- docs/react/adapters/solid-query.md | 8 +- docs/react/adapters/vue-query.md | 4 +- docs/react/community/tkdodos-blog.md | 2 +- .../guides/background-fetching-indicators.md | 4 +- docs/react/guides/caching.md | 2 +- docs/react/guides/dependent-queries.md | 7 +- docs/react/guides/disabling-queries.md | 14 +- docs/react/guides/infinite-queries.md | 2 +- docs/react/guides/migrating-to-v5.md | 10 ++ docs/react/guides/mutations.md | 4 +- docs/react/guides/network-mode.md | 4 +- docs/react/guides/paginated-queries.md | 8 +- docs/react/guides/queries.md | 16 +- docs/react/overview.md | 4 +- docs/react/reference/useInfiniteQuery.md | 4 +- docs/react/reference/useMutation.md | 6 +- docs/react/reference/useQuery.md | 23 ++- docs/solid/overview.md | 8 +- .../guides/background-fetching-indicators.md | 4 +- docs/vue/guides/disabling-queries.md | 2 +- docs/vue/guides/infinite-queries.md | 4 +- docs/vue/guides/mutations.md | 4 +- docs/vue/guides/paginated-queries.md | 4 +- docs/vue/guides/queries.md | 6 +- docs/vue/installation.md | 2 +- docs/vue/quick-start.md | 4 +- .../react/nextjs/components/PostList/index.js | 4 +- examples/react/offline/src/App.jsx | 6 +- .../pages/index.tsx | 4 +- examples/react/prefetching/pages/index.js | 4 +- .../src/screens/MovieDetailsScreen.tsx | 4 +- .../src/screens/MoviesListScreen.tsx | 4 +- examples/react/simple/src/index.jsx | 4 +- examples/solid/simple/src/index.tsx | 2 +- .../auto-refetching/src/routes/+page.svelte | 2 +- examples/svelte/basic/src/lib/Post.svelte | 2 +- .../src/lib/LoadMore.svelte | 2 +- .../src/routes/+page.svelte | 4 +- examples/svelte/simple/src/lib/Simple.svelte | 2 +- examples/svelte/ssr/src/lib/Post.svelte | 2 +- examples/vue/2.6-basic/src/Post.vue | 6 +- examples/vue/2.6-basic/src/Posts.vue | 6 +- examples/vue/basic/src/Post.vue | 6 +- examples/vue/basic/src/Posts.vue | 6 +- examples/vue/dependent-queries/src/Post.vue | 6 +- examples/vue/dependent-queries/src/Posts.vue | 6 +- examples/vue/persister/src/Post.vue | 6 +- examples/vue/persister/src/Posts.vue | 6 +- packages/query-core/src/mutation.ts | 18 +- packages/query-core/src/mutationObserver.ts | 2 +- packages/query-core/src/query.ts | 4 +- packages/query-core/src/queryObserver.ts | 15 +- .../src/tests/mutationCache.test.tsx | 6 +- .../query-core/src/tests/mutations.test.tsx | 30 ++-- .../src/tests/queriesObserver.test.tsx | 58 +++---- packages/query-core/src/tests/query.test.tsx | 10 +- .../query-core/src/tests/queryClient.test.tsx | 6 +- .../src/tests/queryObserver.test.tsx | 14 +- packages/query-core/src/types.ts | 41 +++-- packages/query-core/src/utils.ts | 2 +- .../PersistQueryClientProvider.test.tsx | 10 +- .../QueryResetErrorBoundary.test.tsx | 2 +- .../src/__tests__/ssr-hydration.test.tsx | 4 +- .../react-query/src/__tests__/ssr.test.tsx | 2 +- .../src/__tests__/suspense.test.tsx | 2 +- .../src/__tests__/useInfiniteQuery.test.tsx | 8 +- .../src/__tests__/useMutation.test.tsx | 30 ++-- .../src/__tests__/useQuery.test.tsx | 160 +++++++++--------- .../__tests__/createInfiniteQuery.test.tsx | 8 +- .../src/__tests__/createMutation.test.tsx | 30 ++-- .../src/__tests__/createQuery.test.tsx | 131 +++++++------- packages/solid-query/src/createBaseQuery.ts | 2 +- .../src/__tests__/CreateQuery.svelte | 2 +- .../src/__tests__/useInfiniteQuery.test.ts | 2 +- .../__tests__/useInfiniteQuery.types.test.tsx | 4 +- .../src/__tests__/useIsFetching.test.ts | 6 +- .../src/__tests__/useMutation.test.ts | 14 +- .../src/__tests__/useMutation.types.test.tsx | 4 +- .../src/__tests__/useQueries.test.ts | 28 +-- .../vue-query/src/__tests__/useQuery.test.ts | 22 +-- .../src/__tests__/useQuery.types.test.tsx | 4 +- 82 files changed, 488 insertions(+), 449 deletions(-) diff --git a/docs/react/adapters/react-query.md b/docs/react/adapters/react-query.md index 226effa957..31a67e9c5f 100644 --- a/docs/react/adapters/react-query.md +++ b/docs/react/adapters/react-query.md @@ -16,7 +16,7 @@ function Example() { return (
    - {query.isLoading + {query.isPending ? 'Loading...' : query.isError ? 'Error!' diff --git a/docs/react/adapters/solid-query.md b/docs/react/adapters/solid-query.md index 3a4dbf5e96..b42bd624f4 100644 --- a/docs/react/adapters/solid-query.md +++ b/docs/react/adapters/solid-query.md @@ -21,7 +21,7 @@ function Example() { return (
    - +

    Loading...

    @@ -126,7 +126,7 @@ export default function App() { function Example() { // ❌ react version -- supports destructing outside reactive context - // const { isLoading, error, data } = useQuery({ + // const { isPending, error, data } = useQuery({ // queryKey: ['repoData'], () => // queryFn: fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => // res.json() @@ -145,7 +145,7 @@ function Example() { // ✅ access query properties in JSX reactive context return ( - Loading... + Loading... Error: {query.error.message}
    @@ -190,7 +190,7 @@ function Example() { return (
    - +

    Loading...

    diff --git a/docs/react/adapters/vue-query.md b/docs/react/adapters/vue-query.md index 09a822b48d..9c5d7e64d1 100644 --- a/docs/react/adapters/vue-query.md +++ b/docs/react/adapters/vue-query.md @@ -20,7 +20,7 @@ import { useQueryClient, useQuery, useMutation } from "@tanstack/vue-query"; const queryClient = useQueryClient(); // Query -const { isLoading, isError, data, error } = useQuery({ queryKey: ['todos'], queryFn: getTodos }); +const { isPending, isError, data, error } = useQuery({ queryKey: ['todos'], queryFn: getTodos }); // Mutation const mutation = useMutation({ @@ -40,7 +40,7 @@ function onButtonClick() {