Skip to content

Commit e339157

Browse files
feat(query): prevent placeholder from changing the state
1 parent 2b1939f commit e339157

File tree

3 files changed

+16
-17
lines changed

3 files changed

+16
-17
lines changed

src/query-store.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ export interface UseQueryEntry<TResult = unknown, TError = unknown> {
7676
options: UseQueryOptionsWithDefaults<TResult, TError> | null
7777
// TODO: ideally shouldn't be null, there should be different kind of types
7878

79+
// TODO: remove optional type (`undefined` only)?
80+
placeholderData?: TResult | undefined
81+
7982
/**
8083
* Whether the data is stale or not, requires `options.staleTime` to be set.
8184
*/
@@ -160,6 +163,7 @@ export function queryEntry_removeDep(
160163
* @param error - initial error to set
161164
* @param when - when the data was fetched the last time. defaults to 0, meaning it's stale
162165
*/
166+
// TODO: init placeholder if it is not a function?
163167
export function createQueryEntry<TResult = unknown, TError = ErrorDefault>(
164168
key: EntryNodeKey[],
165169
initialData?: TResult,
@@ -501,10 +505,8 @@ export const useQueryCache = defineStore(QUERY_STORE_ID, ({ action }) => {
501505
<TResult, TError>(
502506
entry: UseQueryEntry<TResult, TError>,
503507
state: DataState<TResult, TError>,
504-
resetWhen?: boolean,
505508
) => {
506509
entry.state.value = state
507-
if (resetWhen) entry.when = Date.now()
508510
},
509511
)
510512

src/use-query.spec.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ describe('useQuery', () => {
456456
expect(query).toHaveBeenCalledTimes(1)
457457
})
458458

459-
it('uses the placeholder data if configured while refetching', async () => {
459+
it('uses the placeholder data (if configured) as fallback while fetching', async () => {
460460
const { wrapper, query } = mountDynamicKey({
461461
query: async () => {
462462
await new Promise((res) => setTimeout(res, 100))
@@ -482,22 +482,23 @@ describe('useQuery', () => {
482482
expect(dataId1).not.toBe(dataId0)
483483

484484
await wrapper.vm.setId(0)
485-
// Data is not stale: placeholder data is not used, catched data is used
485+
// There is fresh cached data: placeholder data is not used
486486
expect(wrapper.vm.data).toBe(dataId0)
487487

488488
vi.advanceTimersByTime(1001)
489-
490489
await wrapper.vm.setId(1)
491-
// Data is stale: placeholder data is used
492-
expect(wrapper.vm.data).toBe(dataId0)
490+
// There is stale cached data: placeholder data is not used either (stale data is used, as usual)
491+
expect(wrapper.vm.data).toBe(dataId1)
493492

494493
vi.advanceTimersByTime(100)
495494
await flushPromises()
496495
// Refetch data is used
497-
expect(dataId1).not.toBe(dataId0)
496+
expect(wrapper.vm.data).not.toBe(dataId1)
498497
expect(query).toHaveBeenCalledTimes(3)
499498
})
500499

500+
it.todo('uses the placeholder data (if configured) as fallback in case of error')
501+
501502
it('refreshes the data if mounted and the key changes', async () => {
502503
const { wrapper, query } = mountDynamicKey({
503504
initialId: 0,

src/use-query.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export function useQuery<TResult, TError = ErrorDefault>(
152152
state: computed(() => entry.value.state.value),
153153

154154
status: computed(() => entry.value.state.value.status),
155-
data: computed(() => entry.value.state.value.data),
155+
data: computed(() => entry.value.state.value.data || entry.value.placeholderData),
156156
error: computed(() => entry.value.state.value.error),
157157
asyncStatus: computed(() => entry.value.asyncStatus.value),
158158

@@ -200,14 +200,10 @@ export function useQuery<TResult, TError = ErrorDefault>(
200200
entry,
201201
(entry, previousEntry) => {
202202
if (!isActive) return
203-
if (entry.stale && entry.options?.placeholderData && previousEntry?.state.value.status === 'success') {
204-
cacheEntries.setEntryState(entry, {
205-
// TODO: pending?
206-
status: 'success',
207-
// @ts-expect-error: TODO: fix this
208-
data: (typeof entry.options.placeholderData === 'function') ? entry.options.placeholderData(previousEntry.state.value.data) : previousEntry.state.value.data,
209-
error: null,
210-
}, false)
203+
// TODO: handle HMR in case where the placeholder is removed from the options?
204+
if (entry.options?.placeholderData && previousEntry?.state.value.status === 'success') {
205+
// @ts-expect-error: TODO: fix this
206+
entry.placeholderData = (typeof entry.options.placeholderData === 'function') ? entry.options.placeholderData(previousEntry.state.value.data) : previousEntry.state.value.data
211207
}
212208
if (previousEntry) {
213209
queryEntry_removeDep(previousEntry, hasCurrentInstance, cacheEntries)

0 commit comments

Comments
 (0)