Skip to content

Commit

Permalink
feat(use-query): Add enabled option (#43)
Browse files Browse the repository at this point in the history
* feat(use-query): Add enabled option

* refactor: change types and test names, add test, replace refetch with refresh

* refactor: move if statement, improve type checking

* refactor: remove redundant check

---------

Co-authored-by: Vadim Kruglov <vadim.kruglov@libertexgroup.com>
  • Loading branch information
quiteeasy and Vadim Kruglov committed May 16, 2024
1 parent 7e54b65 commit 1b755c5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
7 changes: 7 additions & 0 deletions src/query-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export interface UseQueryOptions<TResult = unknown, TError = ErrorDefault> {
*/
query: (context: UseQueryFnContext) => Promise<TResult>

/**
* Reactive boolean, indicating whether refetch/refresh should be called internally,
* calling refetch when toggled to true
*/
enabled?: MaybeRefOrGetter<boolean>

/**
* Time in ms after which the data is considered stale and will be refreshed on next read
*/
Expand Down Expand Up @@ -106,6 +112,7 @@ export const USE_QUERY_DEFAULTS = {
refetchOnWindowFocus: true as _RefetchOnControl,
refetchOnReconnect: true as _RefetchOnControl,
refetchOnMount: true as _RefetchOnControl,
enabled: true as MaybeRefOrGetter<boolean>,
// as any to simplify the typing with generics
transformError: (error: unknown) => error as any,
} satisfies Partial<UseQueryOptions>
Expand Down
55 changes: 55 additions & 0 deletions src/use-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,61 @@ describe('useQuery', () => {
expect(query).toHaveBeenCalledTimes(2)
})

it('does not automatically refresh when enabled is false', async () => {
const { wrapper, query } = mountDynamicKey({ enabled: false })

await flushPromises()
expect(query).toBeCalledTimes(0)

// should refresh manually
await wrapper.vm.refresh()
await flushPromises()
expect(query).toBeCalledTimes(1)

await wrapper.vm.setId(2)
expect(query).toBeCalledTimes(1)
})

it('triggers the query function when enabled becomes true', async () => {
const enabled = ref(false)
const { wrapper, query } = mountDynamicKey({ enabled })

await flushPromises()
expect(query).toBeCalledTimes(0)

enabled.value = true
await nextTick()
await flushPromises()
expect(query).toBeCalledTimes(1)

await wrapper.vm.setId(2)
expect(query).toBeCalledTimes(2)

// no refetch when value is not toggled
enabled.value = true
await nextTick()
await flushPromises()
expect(query).toBeCalledTimes(2)
})

it('does not trigger the query function when enabled becomes true if data is not stale', async () => {
const enabled = ref(true)
const { query } = mountDynamicKey({ enabled })

await flushPromises()
expect(query).toBeCalledTimes(1)

enabled.value = false
await nextTick()
await flushPromises()
expect(query).toBeCalledTimes(1)

enabled.value = true
await nextTick()
await flushPromises()
expect(query).toBeCalledTimes(1)
})

it.todo('can avoid throwing', async () => {
const { wrapper, query } = mountSimple({
staleTime: 0,
Expand Down
30 changes: 20 additions & 10 deletions src/use-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export function useQuery<TResult, TError = ErrorDefault>(
const refresh = () => store.refresh(entry.value)
const refetch = () => store.refetch(entry.value)

if (typeof options.enabled !== 'boolean') {
watch(options.enabled, (newEnabled, oldEnabled) => {
// add test case why we check !oldEnabled
// add test case when we update entry, add test case when we update enabled
if (!oldEnabled && newEnabled) refresh()
})
}

const queryReturn = {
data: computedRef(() => entry.value.data),
error: computedRef(() => entry.value.error),
Expand All @@ -90,7 +98,7 @@ export function useQuery<TResult, TError = ErrorDefault>(
if (hasCurrentInstance) {
// only happens on server, app awaits this
onServerPrefetch(async () => {
await refresh()
if (toValue(options.enabled)) await refresh()
// TODO: after adding a test, remove these lines and refactor the const queryReturn to just a return statement
// NOTE: workaround to https://github.com/vuejs/core/issues/5300
// eslint-disable-next-line no-unused-expressions, no-sequences
Expand Down Expand Up @@ -137,7 +145,7 @@ export function useQuery<TResult, TError = ErrorDefault>(
removeDep(previousEntry)
}
addDep(entry)
refresh()
if (toValue(options.enabled)) refresh()
},
{ immediate: true },
)
Expand All @@ -148,10 +156,10 @@ export function useQuery<TResult, TError = ErrorDefault>(
// TODO: optimize so it doesn't refresh if we are hydrating
onMounted(() => {
if (
options.refetchOnMount
(options.refetchOnMount
// always fetch initially if no vaule is present
// TODO: refactor noce we introduce the enabled option
|| queryReturn.status.value === 'pending'
|| queryReturn.status.value === 'pending')
&& toValue(options.enabled)
) {
if (options.refetchOnMount === 'always') {
refetch()
Expand All @@ -172,7 +180,7 @@ export function useQuery<TResult, TError = ErrorDefault>(
if (IS_CLIENT) {
if (options.refetchOnWindowFocus) {
useEventListener(document, 'visibilitychange', () => {
if (document.visibilityState === 'visible') {
if (document.visibilityState === 'visible' && toValue(options.enabled)) {
if (options.refetchOnWindowFocus === 'always') {
refetch()
} else {
Expand All @@ -184,10 +192,12 @@ export function useQuery<TResult, TError = ErrorDefault>(

if (options.refetchOnReconnect) {
useEventListener(window, 'online', () => {
if (options.refetchOnReconnect === 'always') {
refetch()
} else {
refresh()
if (toValue(options.enabled)) {
if (options.refetchOnReconnect === 'always') {
refetch()
} else {
refresh()
}
}
})
}
Expand Down

0 comments on commit 1b755c5

Please sign in to comment.