Skip to content

Commit

Permalink
perf(vue-query): add shallow flag to improve perf on large datasets (
Browse files Browse the repository at this point in the history
…#7733)

* fix(vue-query): use shallowReactive and shallowReadonly to prevent perforamcne slowdown for large datasets

* Use shallowReadonly and shallowRef in useMutationState too

* fix(vue-query): add shallow option, strip readonly in prod mode

---------

Co-authored-by: Damian Osipiuk <osipiukd+git@gmail.com>
  • Loading branch information
wns3645 and DamianOsipiuk committed Sep 5, 2024
1 parent 0f86b4d commit 9cb5922
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 43 deletions.
15 changes: 12 additions & 3 deletions packages/vue-query/src/useBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {
computed,
getCurrentScope,
onScopeDispose,
reactive,
readonly,
shallowReactive,
shallowReadonly,
toRefs,
watch,
} from 'vue-demi'
Expand Down Expand Up @@ -105,7 +106,7 @@ export function useBaseQuery<
})

const observer = new Observer(client, defaultedOptions.value)
const state = reactive(observer.getCurrentResult())
const state = shallowReactive(observer.getCurrentResult())

let unsubscribe = () => {
// noop
Expand Down Expand Up @@ -201,7 +202,15 @@ export function useBaseQuery<
},
)

const object: any = toRefs(readonly(state))
const readonlyState =
process.env.NODE_ENV === 'production'
? state
: // @ts-expect-error
defaultedOptions.value.shallow
? shallowReadonly(state)
: readonly(state)

const object: any = toRefs(readonlyState)
for (const key in state) {
if (typeof state[key as keyof typeof state] === 'function') {
object[key] = state[key as keyof typeof state]
Expand Down
2 changes: 2 additions & 0 deletions packages/vue-query/src/useInfiniteQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export type UseInfiniteQueryOptions<
TPageParam
>[Property]
>
} & {
shallow?: boolean
}

export type UseInfiniteQueryReturnType<TData, TError> = UseBaseQueryReturnType<
Expand Down
23 changes: 18 additions & 5 deletions packages/vue-query/src/useMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {
computed,
getCurrentScope,
onScopeDispose,
reactive,
readonly,
shallowReactive,
shallowReadonly,
toRefs,
watch,
} from 'vue-demi'
Expand All @@ -26,12 +27,17 @@ type MutationResult<TData, TError, TVariables, TContext> = DistributiveOmit<
'mutate' | 'reset'
>

type UseMutationOptionsBase<TData, TError, TVariables, TContext> =
MutationObserverOptions<TData, TError, TVariables, TContext> & {
shallow?: boolean
}

export type UseMutationOptions<
TData = unknown,
TError = DefaultError,
TVariables = void,
TContext = unknown,
> = MaybeRefDeep<MutationObserverOptions<TData, TError, TVariables, TContext>>
> = MaybeRefDeep<UseMutationOptionsBase<TData, TError, TVariables, TContext>>

type MutateSyncFunction<
TData = unknown,
Expand Down Expand Up @@ -61,7 +67,7 @@ export function useMutation<
TContext = unknown,
>(
mutationOptions: MaybeRefDeep<
MutationObserverOptions<TData, TError, TVariables, TContext>
UseMutationOptionsBase<TData, TError, TVariables, TContext>
>,
queryClient?: QueryClient,
): UseMutationReturnType<TData, TError, TVariables, TContext> {
Expand All @@ -78,7 +84,7 @@ export function useMutation<
return client.defaultMutationOptions(cloneDeepUnref(mutationOptions))
})
const observer = new MutationObserver(client, options.value)
const state = reactive(observer.getCurrentResult())
const state = shallowReactive(observer.getCurrentResult())

const unsubscribe = observer.subscribe((result) => {
updateState(state, result)
Expand All @@ -101,7 +107,14 @@ export function useMutation<
unsubscribe()
})

const resultRefs = toRefs(readonly(state)) as unknown as ToRefs<
const readonlyState =
process.env.NODE_ENV === 'production'
? state
: options.value.shallow
? shallowReadonly(state)
: readonly(state)

const resultRefs = toRefs(readonlyState) as ToRefs<
Readonly<MutationResult<TData, TError, TVariables, TContext>>
>

Expand Down
14 changes: 8 additions & 6 deletions packages/vue-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import {
computed,
getCurrentScope,
onScopeDispose,
readonly,
ref,
shallowReadonly,
shallowRef,
watch,
} from 'vue-demi'
import { useQueryClient } from './useQueryClient'
import { cloneDeepUnref } from './utils'
import type { DeepReadonly, Ref } from 'vue-demi'
import type { Ref } from 'vue-demi'
import type {
MutationFilters as MF,
Mutation,
Expand Down Expand Up @@ -68,10 +68,12 @@ function getResult<TResult = MutationState>(
export function useMutationState<TResult = MutationState>(
options: MutationStateOptions<TResult> = {},
queryClient?: QueryClient,
): DeepReadonly<Ref<Array<TResult>>> {
): Readonly<Ref<Array<TResult>>> {
const filters = computed(() => cloneDeepUnref(options.filters))
const mutationCache = (queryClient || useQueryClient()).getMutationCache()
const state = ref(getResult(mutationCache, options)) as Ref<Array<TResult>>
const state = shallowRef(getResult(mutationCache, options)) as Ref<
Array<TResult>
>
const unsubscribe = mutationCache.subscribe(() => {
const result = getResult(mutationCache, options)
state.value = result
Expand All @@ -85,5 +87,5 @@ export function useMutationState<TResult = MutationState>(
unsubscribe()
})

return readonly(state)
return shallowReadonly(state)
}
8 changes: 7 additions & 1 deletion packages/vue-query/src/useQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getCurrentScope,
onScopeDispose,
readonly,
shallowReadonly,
shallowRef,
unref,
watch,
Expand Down Expand Up @@ -256,6 +257,7 @@ export function useQueries<
}: {
queries: MaybeRefDeep<UseQueriesOptionsArg<T>>
combine?: (result: UseQueriesResults<T>) => TCombinedResult
shallow?: boolean
},
queryClient?: QueryClient,
): Readonly<Ref<TCombinedResult>> {
Expand Down Expand Up @@ -348,5 +350,9 @@ export function useQueries<
unsubscribe()
})

return readonly(state) as Readonly<Ref<TCombinedResult>>
return process.env.NODE_ENV === 'production'
? state
: options.shallow
? shallowReadonly(state)
: (readonly(state) as Readonly<Ref<TCombinedResult>>)
}
58 changes: 31 additions & 27 deletions packages/vue-query/src/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,37 @@ export type UseQueryOptions<
TData = TQueryFnData,
TQueryData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = MaybeRef<{
[Property in keyof QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>]: Property extends 'enabled'
? MaybeRefOrGetter<
QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
>
: MaybeRefDeep<
QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
>
}>
> = MaybeRef<
{
[Property in keyof QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>]: Property extends 'enabled'
? MaybeRefOrGetter<
QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
>
: MaybeRefDeep<
QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
>
} & {
shallow?: boolean
}
>

export type UndefinedInitialQueryOptions<
TQueryFnData = unknown,
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-query/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function getClientKey(key?: string) {
}

export function updateState(
state: Record<string, unknown>,
state: Record<string, any>,
update: Record<string, any>,
): void {
Object.keys(state).forEach((key) => {
Expand Down

0 comments on commit 9cb5922

Please sign in to comment.