From b78c8d8662fba8359bbee8eed5c1ecaaf6e9605e Mon Sep 17 00:00:00 2001 From: Daniel Reinoso Date: Wed, 14 Sep 2022 13:11:13 -0300 Subject: [PATCH 01/24] fix(query): create QueryObserver with initial options --- src/query/atomWithQuery.ts | 40 ++++++++++++---------- tests/query/atomWithQuery.test.tsx | 54 +++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 5b12e8d28d..7558f61397 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -1,4 +1,4 @@ -import { QueryObserver } from '@tanstack/query-core' +import { QueryClient, QueryObserver } from '@tanstack/query-core' import type { QueryKey, QueryObserverOptions, @@ -99,25 +99,30 @@ export function atomWithQuery< > type Result = QueryObserverResult - const observerAtom = atom((get) => { - const queryClient = getQueryClient(get) - const defaultedOptions = queryClient.defaultQueryOptions< + let observer: + | QueryObserver + | undefined + function getObserver( + queryClient: QueryClient, + options?: AtomWithQueryOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey - >() - const observer = new QueryObserver< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >(queryClient, defaultedOptions) + > + ) { + if (!observer) { + observer = new QueryObserver< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >(queryClient, queryClient.defaultQueryOptions(options)) + } return observer - }) - + } const queryDataAtom: WritableAtom< { options: Options @@ -131,9 +136,9 @@ export function atomWithQuery< const queryClient = getQueryClient(get) const options = typeof createQuery === 'function' ? createQuery(get) : createQuery - const defaultedOptions = queryClient.defaultQueryOptions(options) - const observer = get(observerAtom) + const observer = getObserver(queryClient, options) observer.destroy() + const defaultedOptions = queryClient.defaultQueryOptions(options) observer.setOptions(defaultedOptions) const initialResult = observer.getCurrentResult() @@ -187,11 +192,12 @@ export function atomWithQuery< return { options, resultAtom, unsubIfNotMounted } }, (get, set, action) => { - const observer = get(observerAtom) const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) if (options.enabled === false) { return } + const queryClient = getQueryClient(get) + const observer = getObserver(queryClient, options) switch (action.type) { case 'refetch': { set(resultAtom, new Promise(() => {})) // infinite pending diff --git a/tests/query/atomWithQuery.test.tsx b/tests/query/atomWithQuery.test.tsx index 584e821480..75d1ac2da4 100644 --- a/tests/query/atomWithQuery.test.tsx +++ b/tests/query/atomWithQuery.test.tsx @@ -1,5 +1,13 @@ -import { Component, StrictMode, Suspense, useContext, useState } from 'react' +import { + Component, + StrictMode, + Suspense, + useContext, + useState, + useSyncExternalStore, +} from 'react' import type { ReactNode } from 'react' +import { QueryClient } from '@tanstack/query-core' import { fireEvent, render } from '@testing-library/react' import { atom, @@ -722,3 +730,47 @@ describe('error handling', () => { await findByText('count: 3') }) }) + +it('query expected QueryCache test', async () => { + const queryClient = new QueryClient() + const countAtom = atomWithQuery( + () => ({ + queryKey: ['count6'], + queryFn: async () => { + return await fakeFetch({ count: 0 }, false, 100) + }, + }), + () => queryClient + ) + const Counter = () => { + const [ + { + response: { count }, + }, + ] = useAtom(countAtom) + const cachedQueries = useSyncExternalStore( + queryClient.getQueryCache().subscribe, + () => queryClient.getQueryCache().getAll() + ) + return ( + <> +
count: {count}
+
queries in cache: {cachedQueries.length}
+ + ) + } + + const { findByText } = render( + + + + + + + + ) + + await findByText('loading') + await findByText('count: 0') + await findByText('queries in cache: 1') +}) From b5d2eec8a91256281eea58fdf4879f5bdfef66a2 Mon Sep 17 00:00:00 2001 From: Daniel Reinoso Date: Thu, 15 Sep 2022 09:42:02 -0300 Subject: [PATCH 02/24] Remove use of useSES --- tests/query/atomWithQuery.test.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/query/atomWithQuery.test.tsx b/tests/query/atomWithQuery.test.tsx index 75d1ac2da4..264fabe0c4 100644 --- a/tests/query/atomWithQuery.test.tsx +++ b/tests/query/atomWithQuery.test.tsx @@ -748,14 +748,10 @@ it('query expected QueryCache test', async () => { response: { count }, }, ] = useAtom(countAtom) - const cachedQueries = useSyncExternalStore( - queryClient.getQueryCache().subscribe, - () => queryClient.getQueryCache().getAll() - ) + return ( <>
count: {count}
-
queries in cache: {cachedQueries.length}
) } @@ -772,5 +768,5 @@ it('query expected QueryCache test', async () => { await findByText('loading') await findByText('count: 0') - await findByText('queries in cache: 1') + expect(queryClient.getQueryCache().getAll().length).toBe(1) }) From 712e61a525447d71a20df211fc734430fe1bec37 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 15 Sep 2022 22:24:57 +0900 Subject: [PATCH 03/24] remove unused warning --- tests/query/atomWithQuery.test.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/query/atomWithQuery.test.tsx b/tests/query/atomWithQuery.test.tsx index 264fabe0c4..e1c56309bc 100644 --- a/tests/query/atomWithQuery.test.tsx +++ b/tests/query/atomWithQuery.test.tsx @@ -1,11 +1,4 @@ -import { - Component, - StrictMode, - Suspense, - useContext, - useState, - useSyncExternalStore, -} from 'react' +import { Component, StrictMode, Suspense, useContext, useState } from 'react' import type { ReactNode } from 'react' import { QueryClient } from '@tanstack/query-core' import { fireEvent, render } from '@testing-library/react' From fa100817e7664d660e230ec46eed591d70462943 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 15 Sep 2022 22:47:01 +0900 Subject: [PATCH 04/24] wip: extract only queryKey --- src/query/atomWithQuery.ts | 47 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 7558f61397..68be4ab89a 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -1,4 +1,4 @@ -import { QueryClient, QueryObserver } from '@tanstack/query-core' +import { QueryObserver } from '@tanstack/query-core' import type { QueryKey, QueryObserverOptions, @@ -99,30 +99,32 @@ export function atomWithQuery< > type Result = QueryObserverResult - let observer: - | QueryObserver - | undefined - function getObserver( - queryClient: QueryClient, - options?: AtomWithQueryOptions< + const queryKeyAtom = atom((get) => { + const options = + typeof createQuery === 'function' ? createQuery(get) : createQuery + return options.queryKey + }) + + const observerAtom = atom((get) => { + const queryClient = getQueryClient(get) + const queryKey = get(queryKeyAtom) + const defaultedOptions = queryClient.defaultQueryOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey - > - ) { - if (!observer) { - observer = new QueryObserver< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >(queryClient, queryClient.defaultQueryOptions(options)) - } + >({ queryKey }) + const observer = new QueryObserver< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >(queryClient, defaultedOptions) return observer - } + }) + const queryDataAtom: WritableAtom< { options: Options @@ -136,9 +138,9 @@ export function atomWithQuery< const queryClient = getQueryClient(get) const options = typeof createQuery === 'function' ? createQuery(get) : createQuery - const observer = getObserver(queryClient, options) - observer.destroy() const defaultedOptions = queryClient.defaultQueryOptions(options) + const observer = get(observerAtom) + observer.destroy() observer.setOptions(defaultedOptions) const initialResult = observer.getCurrentResult() @@ -192,12 +194,11 @@ export function atomWithQuery< return { options, resultAtom, unsubIfNotMounted } }, (get, set, action) => { + const observer = get(observerAtom) const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) if (options.enabled === false) { return } - const queryClient = getQueryClient(get) - const observer = getObserver(queryClient, options) switch (action.type) { case 'refetch': { set(resultAtom, new Promise(() => {})) // infinite pending From 49ce3b1dc4e48f833183e8e7641a2ace03e54b47 Mon Sep 17 00:00:00 2001 From: Daniel Reinoso Date: Sat, 17 Sep 2022 09:39:01 -0300 Subject: [PATCH 05/24] Keep initial options ref per QueryClient --- src/query/atomWithQuery.ts | 42 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 68be4ab89a..1ee9957659 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -1,4 +1,4 @@ -import { QueryObserver } from '@tanstack/query-core' +import { QueryClient, QueryObserver } from '@tanstack/query-core' import type { QueryKey, QueryObserverOptions, @@ -99,29 +99,41 @@ export function atomWithQuery< > type Result = QueryObserverResult - const queryKeyAtom = atom((get) => { - const options = + const initialOptionsRefAtom = atom( + () => + ({} as { + prev?: { + queryClient: QueryClient + initialOptions: Options + } + }) + ) + + const initialOptionsAtom = atom((get) => { + const queryClient = getQueryClient(get) + const ref = get(initialOptionsRefAtom) + if ('prev' in ref && Object.is(queryClient, ref.prev.queryClient)) { + return ref.prev.initialOptions + } + const initialOptions = typeof createQuery === 'function' ? createQuery(get) : createQuery - return options.queryKey + ref.prev = { + queryClient, + initialOptions, + } + return initialOptions }) const observerAtom = atom((get) => { const queryClient = getQueryClient(get) - const queryKey = get(queryKeyAtom) - const defaultedOptions = queryClient.defaultQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >({ queryKey }) + const initialOptions = get(initialOptionsAtom) const observer = new QueryObserver< TQueryFnData, TError, TData, TQueryData, TQueryKey - >(queryClient, defaultedOptions) + >(queryClient, initialOptions) return observer }) @@ -135,13 +147,11 @@ export function atomWithQuery< void | Promise > = atom( (get) => { - const queryClient = getQueryClient(get) const options = typeof createQuery === 'function' ? createQuery(get) : createQuery - const defaultedOptions = queryClient.defaultQueryOptions(options) const observer = get(observerAtom) observer.destroy() - observer.setOptions(defaultedOptions) + observer.setOptions(options) const initialResult = observer.getCurrentResult() let resolve: ((result: Result) => void) | null = null From 7602b1b8f09d28716abd7a14b0c9ae9395ae4179 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 16:29:42 +0900 Subject: [PATCH 06/24] refactor with observerMap --- src/query/atomWithQuery.ts | 58 ++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 1ee9957659..05c035f467 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -99,43 +99,33 @@ export function atomWithQuery< > type Result = QueryObserverResult - const initialOptionsRefAtom = atom( - () => - ({} as { - prev?: { - queryClient: QueryClient - initialOptions: Options - } - }) - ) - - const initialOptionsAtom = atom((get) => { - const queryClient = getQueryClient(get) - const ref = get(initialOptionsRefAtom) - if ('prev' in ref && Object.is(queryClient, ref.prev.queryClient)) { - return ref.prev.initialOptions - } - const initialOptions = - typeof createQuery === 'function' ? createQuery(get) : createQuery - ref.prev = { - queryClient, - initialOptions, - } - return initialOptions - }) - - const observerAtom = atom((get) => { - const queryClient = getQueryClient(get) - const initialOptions = get(initialOptionsAtom) - const observer = new QueryObserver< + const observerMap = new WeakMap< + QueryClient, + QueryObserver + >() + const getObserver = ( + queryClient: QueryClient, + options: QueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey - >(queryClient, initialOptions) + > + ) => { + let observer = observerMap.get(queryClient) + if (!observer) { + observer = new QueryObserver< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >(queryClient, options) + observerMap.set(queryClient, observer) + } return observer - }) + } const queryDataAtom: WritableAtom< { @@ -149,7 +139,8 @@ export function atomWithQuery< (get) => { const options = typeof createQuery === 'function' ? createQuery(get) : createQuery - const observer = get(observerAtom) + const queryClient = getQueryClient(get) + const observer = getObserver(queryClient, options) observer.destroy() observer.setOptions(options) const initialResult = observer.getCurrentResult() @@ -204,11 +195,12 @@ export function atomWithQuery< return { options, resultAtom, unsubIfNotMounted } }, (get, set, action) => { - const observer = get(observerAtom) const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) if (options.enabled === false) { return } + const queryClient = getQueryClient(get) + const observer = getObserver(queryClient, options) switch (action.type) { case 'refetch': { set(resultAtom, new Promise(() => {})) // infinite pending From feb402f1dca75afccb3d24f8bdb3cc0ae1244d93 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 16:49:46 +0900 Subject: [PATCH 07/24] refactor --- src/query/atomWithQuery.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 05c035f467..1d732b39b4 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -99,11 +99,11 @@ export function atomWithQuery< > type Result = QueryObserverResult - const observerMap = new WeakMap< + const observerCache = new WeakMap< QueryClient, QueryObserver >() - const getObserver = ( + const createObserver = ( queryClient: QueryClient, options: QueryObserverOptions< TQueryFnData, @@ -113,7 +113,7 @@ export function atomWithQuery< TQueryKey > ) => { - let observer = observerMap.get(queryClient) + let observer = observerCache.get(queryClient) if (!observer) { observer = new QueryObserver< TQueryFnData, @@ -122,7 +122,7 @@ export function atomWithQuery< TQueryData, TQueryKey >(queryClient, options) - observerMap.set(queryClient, observer) + observerCache.set(queryClient, observer) } return observer } @@ -140,7 +140,7 @@ export function atomWithQuery< const options = typeof createQuery === 'function' ? createQuery(get) : createQuery const queryClient = getQueryClient(get) - const observer = getObserver(queryClient, options) + const observer = createObserver(queryClient, options) observer.destroy() observer.setOptions(options) const initialResult = observer.getCurrentResult() @@ -200,7 +200,7 @@ export function atomWithQuery< return } const queryClient = getQueryClient(get) - const observer = getObserver(queryClient, options) + const observer = createObserver(queryClient, options) switch (action.type) { case 'refetch': { set(resultAtom, new Promise(() => {})) // infinite pending From db515cd46652a5325e22e2f6e67c714bfa5cc786 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 18:05:35 +0900 Subject: [PATCH 08/24] wip: refactor --- src/query/atomWithQuery.ts | 166 ++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 84 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 1d732b39b4..b78524280d 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -5,10 +5,12 @@ import type { QueryObserverResult, } from '@tanstack/query-core' import { atom } from 'jotai' -import type { PrimitiveAtom, WritableAtom } from 'jotai' +import type { WritableAtom } from 'jotai' import { queryClientAtom } from './queryClientAtom' import type { CreateQueryOptions, GetQueryClient } from './types' +type Timeout = ReturnType + type AtomWithQueryAction = { type: 'refetch' } @@ -90,13 +92,6 @@ export function atomWithQuery< >, getQueryClient: GetQueryClient = (get) => get(queryClientAtom) ): WritableAtom> { - type Options = AtomWithQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > type Result = QueryObserverResult const observerCache = new WeakMap< @@ -127,74 +122,93 @@ export function atomWithQuery< return observer } - const queryDataAtom: WritableAtom< - { - options: Options - resultAtom: PrimitiveAtom> - unsubIfNotMounted: () => void - }, - AtomWithQueryAction, - void | Promise - > = atom( - (get) => { - const options = - typeof createQuery === 'function' ? createQuery(get) : createQuery - const queryClient = getQueryClient(get) - const observer = createObserver(queryClient, options) - observer.destroy() - observer.setOptions(options) - const initialResult = observer.getCurrentResult() + const queryDataAtom = atom((get) => { + const options = + typeof createQuery === 'function' ? createQuery(get) : createQuery + const queryClient = getQueryClient(get) + const observer = createObserver(queryClient, options) + observer.destroy() + observer.setOptions(options) + const initialResult = observer.getCurrentResult() - let resolve: ((result: Result) => void) | null = null - const resultAtom = atom>( - initialResult.data === undefined && options.enabled !== false - ? new Promise((r) => { - resolve = r - }) - : initialResult - ) - let setResult: ((result: Result) => void) | null = null - let unsubscribe: (() => void) | null = null - const unsubIfNotMounted = () => { - if (!setResult) { - unsubscribe?.() - unsubscribe = null - } + let resolve: ((result: Result) => void) | null = null + const makePending = () => + new Promise((r) => { + resolve = r + }) + const resultAtom = atom>( + initialResult.data === undefined && options.enabled !== false + ? makePending() + : initialResult + ) + let setResult: ((result: Result) => void) | null = null + const listener = (result: Result) => { + if (result.isFetching || (!result.isError && result.data === undefined)) { + return } - const listener = (result: Result) => { - if ( - result.isFetching || - (!result.isError && result.data === undefined) - ) { - return - } - if (resolve) { - setTimeout(unsubIfNotMounted, 1000) - resolve(result) - resolve = null - } else if (setResult) { - setResult(result) - } else { - throw new Error('setting result without mount') - } + if (!resolve && !setResult) { + throw new Error('setting result without mount') + } + if (resolve) { + setTimeout(unsubIfNotMounted, 1000) + resolve(result) + resolve = null + } + if (setResult) { + setResult(result) + } + } + let unsubscribe: (() => void) | null = null + let timer: Timeout | undefined + const startQuery = () => { + if (unsubscribe) { + clearTimeout(timer) + unsubscribe() } if (options.enabled !== false) { unsubscribe = observer.subscribe(listener) } - resultAtom.onMount = (update) => { - setResult = update - if (options.enabled !== false && !unsubscribe) { - unsubscribe = observer.subscribe(listener) - listener(observer.getCurrentResult()) - } - return () => { - setResult = null - unsubscribe?.() - } + if (!setResult) { + // not mounted yet + timer = setTimeout(() => { + if (unsubscribe) { + unsubscribe() + unsubscribe = null + } + }, 1000) } - return { options, resultAtom, unsubIfNotMounted } + } + startQuery() + const unsubIfNotMounted = () => { + if (!setResult) { + unsubscribe?.() + unsubscribe = null + } + } + resultAtom.onMount = (update) => { + setResult = update + if (options.enabled !== false && !unsubscribe) { + unsubscribe = observer.subscribe(listener) + listener(observer.getCurrentResult()) + } + return () => { + setResult = null + unsubscribe?.() + } + } + return { options, resultAtom, unsubIfNotMounted } + }) + + const queryAtom = atom( + (get) => { + const { resultAtom } = get(queryDataAtom) + const result = get(resultAtom) + if (result.isError) { + throw result.error + } + return result.data }, - (get, set, action) => { + (get, set, action: AtomWithQueryAction) => { const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) if (options.enabled === false) { return @@ -213,21 +227,5 @@ export function atomWithQuery< } ) - const queryAtom = atom< - TData | undefined, - AtomWithQueryAction, - void | Promise - >( - (get) => { - const { resultAtom } = get(queryDataAtom) - const result = get(resultAtom) - if (result.isError) { - throw result.error - } - return result.data - }, - (_get, set, action) => set(queryDataAtom, action) // delegate action - ) - return queryAtom } From b0500e4e85aa1a9d3a8d5e97807e7d68565ed041 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 18:30:02 +0900 Subject: [PATCH 09/24] wip: refactor 2 --- src/query/atomWithQuery.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index b78524280d..cdf01a43e8 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -150,7 +150,6 @@ export function atomWithQuery< throw new Error('setting result without mount') } if (resolve) { - setTimeout(unsubIfNotMounted, 1000) resolve(result) resolve = null } @@ -160,7 +159,15 @@ export function atomWithQuery< } let unsubscribe: (() => void) | null = null let timer: Timeout | undefined - const startQuery = () => { + const startQuery = (refetch?: boolean) => { + if (refetch) { + if (options.enabled === false) { + return + } + unsubIfNotMounted() + observer.refetch({ cancelRefetch: true }).then(listener) + return + } if (unsubscribe) { clearTimeout(timer) unsubscribe() @@ -187,16 +194,17 @@ export function atomWithQuery< } resultAtom.onMount = (update) => { setResult = update - if (options.enabled !== false && !unsubscribe) { - unsubscribe = observer.subscribe(listener) - listener(observer.getCurrentResult()) + if (unsubscribe) { + clearTimeout(timer) + } else { + startQuery() } return () => { setResult = null unsubscribe?.() } } - return { options, resultAtom, unsubIfNotMounted } + return { options, resultAtom, makePending, startQuery, unsubIfNotMounted } }) const queryAtom = atom( @@ -209,19 +217,11 @@ export function atomWithQuery< return result.data }, (get, set, action: AtomWithQueryAction) => { - const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) - if (options.enabled === false) { - return - } - const queryClient = getQueryClient(get) - const observer = createObserver(queryClient, options) + const { resultAtom, makePending, startQuery } = get(queryDataAtom) switch (action.type) { case 'refetch': { - set(resultAtom, new Promise(() => {})) // infinite pending - unsubIfNotMounted() - return observer.refetch({ cancelRefetch: true }).then((result) => { - set(resultAtom, result) - }) + set(resultAtom, makePending()) + startQuery(true) } } } From e49115011f04f256713669adf11a9dcf0b8d9ea5 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 18:59:14 +0900 Subject: [PATCH 10/24] wip: refactor 3 --- src/query/atomWithQuery.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index cdf01a43e8..7ce7adb1b9 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -161,19 +161,23 @@ export function atomWithQuery< let timer: Timeout | undefined const startQuery = (refetch?: boolean) => { if (refetch) { - if (options.enabled === false) { - return + if (options.enabled !== false) { + if (!setResult && unsubscribe) { + unsubscribe() + unsubscribe = null + } + observer.refetch({ cancelRefetch: true }).then(listener) } - unsubIfNotMounted() - observer.refetch({ cancelRefetch: true }).then(listener) return } if (unsubscribe) { clearTimeout(timer) unsubscribe() + unsubscribe = null } if (options.enabled !== false) { unsubscribe = observer.subscribe(listener) + listener(observer.getCurrentResult()) } if (!setResult) { // not mounted yet @@ -186,12 +190,6 @@ export function atomWithQuery< } } startQuery() - const unsubIfNotMounted = () => { - if (!setResult) { - unsubscribe?.() - unsubscribe = null - } - } resultAtom.onMount = (update) => { setResult = update if (unsubscribe) { @@ -201,10 +199,14 @@ export function atomWithQuery< } return () => { setResult = null - unsubscribe?.() + if (unsubscribe) { + unsubscribe() + // FIXME why does this fail? + // unsubscribe = null + } } } - return { options, resultAtom, makePending, startQuery, unsubIfNotMounted } + return { options, resultAtom, makePending, startQuery } }) const queryAtom = atom( From 6168c6aa5958db1cdd57830d9237eab12980d2d6 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 19:09:54 +0900 Subject: [PATCH 11/24] Revert "wip: refactor 3" This reverts commit e49115011f04f256713669adf11a9dcf0b8d9ea5. --- src/query/atomWithQuery.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 7ce7adb1b9..cdf01a43e8 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -161,23 +161,19 @@ export function atomWithQuery< let timer: Timeout | undefined const startQuery = (refetch?: boolean) => { if (refetch) { - if (options.enabled !== false) { - if (!setResult && unsubscribe) { - unsubscribe() - unsubscribe = null - } - observer.refetch({ cancelRefetch: true }).then(listener) + if (options.enabled === false) { + return } + unsubIfNotMounted() + observer.refetch({ cancelRefetch: true }).then(listener) return } if (unsubscribe) { clearTimeout(timer) unsubscribe() - unsubscribe = null } if (options.enabled !== false) { unsubscribe = observer.subscribe(listener) - listener(observer.getCurrentResult()) } if (!setResult) { // not mounted yet @@ -190,6 +186,12 @@ export function atomWithQuery< } } startQuery() + const unsubIfNotMounted = () => { + if (!setResult) { + unsubscribe?.() + unsubscribe = null + } + } resultAtom.onMount = (update) => { setResult = update if (unsubscribe) { @@ -199,14 +201,10 @@ export function atomWithQuery< } return () => { setResult = null - if (unsubscribe) { - unsubscribe() - // FIXME why does this fail? - // unsubscribe = null - } + unsubscribe?.() } } - return { options, resultAtom, makePending, startQuery } + return { options, resultAtom, makePending, startQuery, unsubIfNotMounted } }) const queryAtom = atom( From f26a00badb04aea33a91a14f00090a900a684784 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 19:10:04 +0900 Subject: [PATCH 12/24] Revert "wip: refactor 2" This reverts commit b0500e4e85aa1a9d3a8d5e97807e7d68565ed041. --- src/query/atomWithQuery.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index cdf01a43e8..b78524280d 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -150,6 +150,7 @@ export function atomWithQuery< throw new Error('setting result without mount') } if (resolve) { + setTimeout(unsubIfNotMounted, 1000) resolve(result) resolve = null } @@ -159,15 +160,7 @@ export function atomWithQuery< } let unsubscribe: (() => void) | null = null let timer: Timeout | undefined - const startQuery = (refetch?: boolean) => { - if (refetch) { - if (options.enabled === false) { - return - } - unsubIfNotMounted() - observer.refetch({ cancelRefetch: true }).then(listener) - return - } + const startQuery = () => { if (unsubscribe) { clearTimeout(timer) unsubscribe() @@ -194,17 +187,16 @@ export function atomWithQuery< } resultAtom.onMount = (update) => { setResult = update - if (unsubscribe) { - clearTimeout(timer) - } else { - startQuery() + if (options.enabled !== false && !unsubscribe) { + unsubscribe = observer.subscribe(listener) + listener(observer.getCurrentResult()) } return () => { setResult = null unsubscribe?.() } } - return { options, resultAtom, makePending, startQuery, unsubIfNotMounted } + return { options, resultAtom, unsubIfNotMounted } }) const queryAtom = atom( @@ -217,11 +209,19 @@ export function atomWithQuery< return result.data }, (get, set, action: AtomWithQueryAction) => { - const { resultAtom, makePending, startQuery } = get(queryDataAtom) + const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) + if (options.enabled === false) { + return + } + const queryClient = getQueryClient(get) + const observer = createObserver(queryClient, options) switch (action.type) { case 'refetch': { - set(resultAtom, makePending()) - startQuery(true) + set(resultAtom, new Promise(() => {})) // infinite pending + unsubIfNotMounted() + return observer.refetch({ cancelRefetch: true }).then((result) => { + set(resultAtom, result) + }) } } } From 18979290af8c05fe6dae508afb0a7e6a5e0776d4 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 19:19:23 +0900 Subject: [PATCH 13/24] wip: use makePending --- src/query/atomWithQuery.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index b78524280d..968bca7317 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -180,8 +180,8 @@ export function atomWithQuery< } startQuery() const unsubIfNotMounted = () => { - if (!setResult) { - unsubscribe?.() + if (!setResult && unsubscribe) { + unsubscribe() unsubscribe = null } } @@ -193,10 +193,14 @@ export function atomWithQuery< } return () => { setResult = null - unsubscribe?.() + if (unsubscribe) { + unsubscribe() + // FIXME why does this fail? + // unsubscribe = null + } } } - return { options, resultAtom, unsubIfNotMounted } + return { options, resultAtom, makePending, unsubIfNotMounted } }) const queryAtom = atom( @@ -209,7 +213,8 @@ export function atomWithQuery< return result.data }, (get, set, action: AtomWithQueryAction) => { - const { options, resultAtom, unsubIfNotMounted } = get(queryDataAtom) + const { options, resultAtom, makePending, unsubIfNotMounted } = + get(queryDataAtom) if (options.enabled === false) { return } @@ -217,7 +222,7 @@ export function atomWithQuery< const observer = createObserver(queryClient, options) switch (action.type) { case 'refetch': { - set(resultAtom, new Promise(() => {})) // infinite pending + set(resultAtom, makePending()) unsubIfNotMounted() return observer.refetch({ cancelRefetch: true }).then((result) => { set(resultAtom, result) From d182c89d3e4e60cd902e6daa82f36762d0e136fb Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 19:28:34 +0900 Subject: [PATCH 14/24] wip: move refetch to startQuery --- src/query/atomWithQuery.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 968bca7317..db3a451147 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -160,7 +160,11 @@ export function atomWithQuery< } let unsubscribe: (() => void) | null = null let timer: Timeout | undefined - const startQuery = () => { + const startQuery = (refetch?: boolean) => { + if (refetch) { + unsubIfNotMounted() + return observer.refetch({ cancelRefetch: true }).then(listener) + } if (unsubscribe) { clearTimeout(timer) unsubscribe() @@ -200,7 +204,7 @@ export function atomWithQuery< } } } - return { options, resultAtom, makePending, unsubIfNotMounted } + return { options, resultAtom, makePending, startQuery } }) const queryAtom = atom( @@ -213,20 +217,15 @@ export function atomWithQuery< return result.data }, (get, set, action: AtomWithQueryAction) => { - const { options, resultAtom, makePending, unsubIfNotMounted } = + const { options, resultAtom, makePending, startQuery } = get(queryDataAtom) if (options.enabled === false) { return } - const queryClient = getQueryClient(get) - const observer = createObserver(queryClient, options) switch (action.type) { case 'refetch': { set(resultAtom, makePending()) - unsubIfNotMounted() - return observer.refetch({ cancelRefetch: true }).then((result) => { - set(resultAtom, result) - }) + return startQuery(true) } } } From 8ee20e96c3bebc9af930f0d8e15776789b3086a2 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 21:16:09 +0900 Subject: [PATCH 15/24] wip: a trial eliminating unsubIfNotMounted --- src/query/atomWithQuery.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index db3a451147..fbc8c4f475 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -150,7 +150,6 @@ export function atomWithQuery< throw new Error('setting result without mount') } if (resolve) { - setTimeout(unsubIfNotMounted, 1000) resolve(result) resolve = null } @@ -162,8 +161,12 @@ export function atomWithQuery< let timer: Timeout | undefined const startQuery = (refetch?: boolean) => { if (refetch) { - unsubIfNotMounted() - return observer.refetch({ cancelRefetch: true }).then(listener) + if (!setResult && unsubscribe) { + unsubscribe() + unsubscribe = null + return observer.refetch({ cancelRefetch: true }).then(listener) + } + return observer.refetch({ cancelRefetch: true }) } if (unsubscribe) { clearTimeout(timer) @@ -183,12 +186,6 @@ export function atomWithQuery< } } startQuery() - const unsubIfNotMounted = () => { - if (!setResult && unsubscribe) { - unsubscribe() - unsubscribe = null - } - } resultAtom.onMount = (update) => { setResult = update if (options.enabled !== false && !unsubscribe) { From c2bc3402f921d734034b48887ff056fccfa7d890 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 21:21:19 +0900 Subject: [PATCH 16/24] wip: fix for react 18 --- src/query/atomWithQuery.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index fbc8c4f475..1a56927edd 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -166,7 +166,9 @@ export function atomWithQuery< unsubscribe = null return observer.refetch({ cancelRefetch: true }).then(listener) } - return observer.refetch({ cancelRefetch: true }) + return observer.refetch({ cancelRefetch: true }).then((result) => { + setResult?.(result) + }) } if (unsubscribe) { clearTimeout(timer) From 7641e8b6bc35473886750ed049c3c25613897af2 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 21:40:32 +0900 Subject: [PATCH 17/24] wip: refactor eliminating options in write --- src/query/atomWithQuery.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 1a56927edd..82dd97bc64 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -161,14 +161,18 @@ export function atomWithQuery< let timer: Timeout | undefined const startQuery = (refetch?: boolean) => { if (refetch) { - if (!setResult && unsubscribe) { - unsubscribe() - unsubscribe = null - return observer.refetch({ cancelRefetch: true }).then(listener) + if (options.enabled !== false) { + if (!setResult && unsubscribe) { + unsubscribe() + unsubscribe = null + observer.refetch({ cancelRefetch: true }).then(listener) + } else { + observer.refetch({ cancelRefetch: true }).then((result) => { + setResult?.(result) + }) + } } - return observer.refetch({ cancelRefetch: true }).then((result) => { - setResult?.(result) - }) + return } if (unsubscribe) { clearTimeout(timer) @@ -203,7 +207,7 @@ export function atomWithQuery< } } } - return { options, resultAtom, makePending, startQuery } + return { resultAtom, makePending, startQuery } }) const queryAtom = atom( @@ -216,11 +220,7 @@ export function atomWithQuery< return result.data }, (get, set, action: AtomWithQueryAction) => { - const { options, resultAtom, makePending, startQuery } = - get(queryDataAtom) - if (options.enabled === false) { - return - } + const { resultAtom, makePending, startQuery } = get(queryDataAtom) switch (action.type) { case 'refetch': { set(resultAtom, makePending()) From 4dc1d85554f22202dd67d6483eabcaf6d10daf79 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 21:52:04 +0900 Subject: [PATCH 18/24] wip: refactor startQuery in onMount --- src/query/atomWithQuery.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 82dd97bc64..d3ec065ae0 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -177,9 +177,11 @@ export function atomWithQuery< if (unsubscribe) { clearTimeout(timer) unsubscribe() + unsubscribe = null } if (options.enabled !== false) { unsubscribe = observer.subscribe(listener) + listener(observer.getCurrentResult()) } if (!setResult) { // not mounted yet @@ -194,9 +196,10 @@ export function atomWithQuery< startQuery() resultAtom.onMount = (update) => { setResult = update - if (options.enabled !== false && !unsubscribe) { - unsubscribe = observer.subscribe(listener) - listener(observer.getCurrentResult()) + if (unsubscribe) { + clearTimeout(timer as Timeout) + } else { + startQuery() } return () => { setResult = null From 774c7f7472eafd44b01c782f3360fa2f880fe7f2 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 22:25:26 +0900 Subject: [PATCH 19/24] wip: subscribe instead of refetch --- src/query/atomWithQuery.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index d3ec065ae0..021ae2bb3a 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -164,8 +164,7 @@ export function atomWithQuery< if (options.enabled !== false) { if (!setResult && unsubscribe) { unsubscribe() - unsubscribe = null - observer.refetch({ cancelRefetch: true }).then(listener) + unsubscribe = observer.subscribe(listener) } else { observer.refetch({ cancelRefetch: true }).then((result) => { setResult?.(result) @@ -197,7 +196,7 @@ export function atomWithQuery< resultAtom.onMount = (update) => { setResult = update if (unsubscribe) { - clearTimeout(timer as Timeout) + clearTimeout(timer) } else { startQuery() } From fc9eac94348bd22f8eff71384b24dc5436742230 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 22:57:20 +0900 Subject: [PATCH 20/24] wip: keep previous data in weak map --- src/query/atomWithQuery.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 021ae2bb3a..c75ea503fc 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -94,6 +94,8 @@ export function atomWithQuery< ): WritableAtom> { type Result = QueryObserverResult + const previousDataCache = new WeakMap() + const observerCache = new WeakMap< QueryClient, QueryObserver @@ -117,7 +119,7 @@ export function atomWithQuery< TQueryData, TQueryKey >(queryClient, options) - observerCache.set(queryClient, observer) + // observerCache.set(queryClient, observer) } return observer } @@ -130,6 +132,12 @@ export function atomWithQuery< observer.destroy() observer.setOptions(options) const initialResult = observer.getCurrentResult() + if ( + initialResult.data === undefined && + previousDataCache.has(queryClient) + ) { + initialResult.data = previousDataCache.get(queryClient) + } let resolve: ((result: Result) => void) | null = null const makePending = () => @@ -156,6 +164,9 @@ export function atomWithQuery< if (setResult) { setResult(result) } + if (options.keepPreviousData && result.data !== undefined) { + previousDataCache.set(queryClient, result.data) + } } let unsubscribe: (() => void) | null = null let timer: Timeout | undefined From b6b18f598275c3a9a085d0a9aa396f0f7e73e734 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 18 Sep 2022 23:13:39 +0900 Subject: [PATCH 21/24] wip: refactor removing observer map --- src/query/atomWithQuery.ts | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index c75ea503fc..802ea8bc49 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -96,41 +96,11 @@ export function atomWithQuery< const previousDataCache = new WeakMap() - const observerCache = new WeakMap< - QueryClient, - QueryObserver - >() - const createObserver = ( - queryClient: QueryClient, - options: QueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > - ) => { - let observer = observerCache.get(queryClient) - if (!observer) { - observer = new QueryObserver< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >(queryClient, options) - // observerCache.set(queryClient, observer) - } - return observer - } - const queryDataAtom = atom((get) => { + const queryClient = getQueryClient(get) const options = typeof createQuery === 'function' ? createQuery(get) : createQuery - const queryClient = getQueryClient(get) - const observer = createObserver(queryClient, options) - observer.destroy() - observer.setOptions(options) + const observer = new QueryObserver(queryClient, options) const initialResult = observer.getCurrentResult() if ( initialResult.data === undefined && From 0aeb9e1898ddac8789f4a85c56358e7af328ac21 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 19 Sep 2022 00:13:06 +0900 Subject: [PATCH 22/24] wip: refactor startQuery a bit --- src/query/atomWithQuery.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 802ea8bc49..f410a3fa26 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -141,16 +141,9 @@ export function atomWithQuery< let unsubscribe: (() => void) | null = null let timer: Timeout | undefined const startQuery = (refetch?: boolean) => { - if (refetch) { + if (refetch && setResult) { if (options.enabled !== false) { - if (!setResult && unsubscribe) { - unsubscribe() - unsubscribe = observer.subscribe(listener) - } else { - observer.refetch({ cancelRefetch: true }).then((result) => { - setResult?.(result) - }) - } + observer.refetch({ cancelRefetch: true }).then(setResult) } return } From 55aaa50a14d560e59cfc3548144a77dd01ea1638 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 19 Sep 2022 00:43:57 +0900 Subject: [PATCH 23/24] wip: refactor types --- src/query/atomWithQuery.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index f410a3fa26..64a7d9d4a2 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -61,11 +61,7 @@ export function atomWithQuery< > >, getQueryClient?: GetQueryClient -): WritableAtom< - TData | TQueryData | undefined, - AtomWithQueryAction, - void | Promise -> +): WritableAtom export function atomWithQuery< TQueryFnData, @@ -78,7 +74,7 @@ export function atomWithQuery< AtomWithQueryOptions >, getQueryClient?: GetQueryClient -): WritableAtom> +): WritableAtom export function atomWithQuery< TQueryFnData, @@ -154,7 +150,6 @@ export function atomWithQuery< } if (options.enabled !== false) { unsubscribe = observer.subscribe(listener) - listener(observer.getCurrentResult()) } if (!setResult) { // not mounted yet @@ -200,7 +195,7 @@ export function atomWithQuery< switch (action.type) { case 'refetch': { set(resultAtom, makePending()) - return startQuery(true) + startQuery(true) } } } From 060e768b3631c95f59174b43762cee7f8f9fdaf0 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 19 Sep 2022 09:53:06 +0900 Subject: [PATCH 24/24] some more refactor and comment --- src/query/atomWithQuery.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/query/atomWithQuery.ts b/src/query/atomWithQuery.ts index 64a7d9d4a2..4be8367623 100644 --- a/src/query/atomWithQuery.ts +++ b/src/query/atomWithQuery.ts @@ -90,6 +90,8 @@ export function atomWithQuery< ): WritableAtom> { type Result = QueryObserverResult + // HACK: having state in atom creator function is not ideal + // because, unlike hooks, it's shared by multiple providers. const previousDataCache = new WeakMap() const queryDataAtom = atom((get) => { @@ -100,6 +102,7 @@ export function atomWithQuery< const initialResult = observer.getCurrentResult() if ( initialResult.data === undefined && + options.keepPreviousData && previousDataCache.has(queryClient) ) { initialResult.data = previousDataCache.get(queryClient) @@ -130,26 +133,24 @@ export function atomWithQuery< if (setResult) { setResult(result) } - if (options.keepPreviousData && result.data !== undefined) { + if (result.data !== undefined) { previousDataCache.set(queryClient, result.data) } } let unsubscribe: (() => void) | null = null let timer: Timeout | undefined - const startQuery = (refetch?: boolean) => { - if (refetch && setResult) { - if (options.enabled !== false) { - observer.refetch({ cancelRefetch: true }).then(setResult) - } - return - } - if (unsubscribe) { + const startQuery = () => { + if (!setResult && unsubscribe) { clearTimeout(timer) unsubscribe() unsubscribe = null } if (options.enabled !== false) { - unsubscribe = observer.subscribe(listener) + if (setResult) { + observer.refetch({ cancelRefetch: true }).then(setResult) + } else { + unsubscribe = observer.subscribe(listener) + } } if (!setResult) { // not mounted yet @@ -195,7 +196,7 @@ export function atomWithQuery< switch (action.type) { case 'refetch': { set(resultAtom, makePending()) - startQuery(true) + startQuery() } } }