feat: enhance entity management and reactivity in vue-sync-engine

This commit is contained in:
2026-06-07 03:57:58 +07:00
parent b2d79b97c1
commit aa3148f4e4
17 changed files with 840 additions and 95 deletions
@@ -1,4 +1,4 @@
import { computed, onScopeDispose, watch, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
import { computed, onScopeDispose, shallowRef, watch, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
import type { InfiniteQueryDef, QueryStatus } from '../core/types'
import { Status } from '../core/flags'
import { hashKey } from '../core/queryKey'
@@ -26,7 +26,9 @@ export function useInfiniteQuery<TArgs, TResp, TPageParam, TResult>(
const initial = toValue(args)
let handle = engine.subscribeQuery(def.name, def.key(initial), initial)
let stateRef = engine.mirror.ensureQuery<InfinitePayload<TResult>>(handle.subId)
// Track the active subId reactively and resolve via ensureQuery (see useQuery for rationale).
const subId = shallowRef(handle.subId)
const state = () => engine.mirror.ensureQuery<InfinitePayload<TResult>>(subId.value).value
if (!def.staticHash) {
watch(
@@ -35,7 +37,7 @@ export function useInfiniteQuery<TArgs, TResp, TPageParam, TResult>(
const next = toValue(args)
const prev = handle
handle = engine.subscribeQuery(def.name, def.key(next), next)
stateRef = engine.mirror.ensureQuery<InfinitePayload<TResult>>(handle.subId)
subId.value = handle.subId
prev.release()
},
)
@@ -44,11 +46,11 @@ export function useInfiniteQuery<TArgs, TResp, TPageParam, TResult>(
onScopeDispose(() => handle.release())
return {
pages: computed(() => stateRef.value.data?.pages ?? []),
pageParams: computed(() => stateRef.value.data?.pageParams ?? []),
status: computed(() => stateRef.value.status),
error: computed(() => stateRef.value.error),
isLoading: computed(() => stateRef.value.status === Status.Pending),
pages: computed(() => state().data?.pages ?? []),
pageParams: computed(() => state().data?.pageParams ?? []),
status: computed(() => state().status),
error: computed(() => state().error),
isLoading: computed(() => state().status === Status.Pending),
fetchNextPage: () => handle.fetchNextPage(),
}
}
@@ -1,4 +1,4 @@
import { computed, onScopeDispose, watch, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
import { computed, onScopeDispose, shallowRef, watch, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
import type { InfiniteQueryDef, QueryDef, QueryStatus } from '../core/types'
import { Status } from '../core/flags'
import { hashKey } from '../core/queryKey'
@@ -21,7 +21,11 @@ export function useQuery<TArgs, TResp, TResult>(
const initial = toValue(args)
let currentHandle = engine.subscribeQuery(def.name, def.key(initial), initial)
let currentRef = engine.mirror.ensureQuery<TResult>(currentHandle.subId)
// Track the active subId reactively (not the state ref itself — passing a ref into shallowRef
// unwraps it). Resolving through ensureQuery() inside each computed means the computed tracks
// both `subId` and the resolved ref, so it re-runs on both an args switch and a data update.
const subId = shallowRef(currentHandle.subId)
const state = () => engine.mirror.ensureQuery<TResult>(subId.value).value
if (!def.staticHash) {
watch(
@@ -30,7 +34,7 @@ export function useQuery<TArgs, TResp, TResult>(
const next = toValue(args)
const prev = currentHandle
currentHandle = engine.subscribeQuery(def.name, def.key(next), next)
currentRef = engine.mirror.ensureQuery<TResult>(currentHandle.subId)
subId.value = currentHandle.subId
prev.release()
},
)
@@ -39,11 +43,11 @@ export function useQuery<TArgs, TResp, TResult>(
onScopeDispose(() => currentHandle.release())
return {
data: computed(() => currentRef.value.data),
status: computed(() => currentRef.value.status),
error: computed(() => currentRef.value.error),
isLoading: computed(() => currentRef.value.status === Status.Pending),
isSuccess: computed(() => currentRef.value.status === Status.Success),
isError: computed(() => currentRef.value.status === Status.Error),
data: computed(() => state().data),
status: computed(() => state().status),
error: computed(() => state().error),
isLoading: computed(() => state().status === Status.Pending),
isSuccess: computed(() => state().status === Status.Success),
isError: computed(() => state().status === Status.Error),
}
}