@tanstack/query-core 5.0.0-alpha.12 → 5.0.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "5.0.0-alpha.12",
3
+ "version": "5.0.0-alpha.19",
4
4
  "description": "The framework agnostic core that powers TanStack Query",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -139,11 +139,11 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
139
139
  >(
140
140
  filters: MutationFilters,
141
141
  ): Mutation<TData, TError, TVariables, TContext> | undefined {
142
- if (typeof filters.exact === 'undefined') {
143
- filters.exact = true
144
- }
142
+ const defaultedFilters = { exact: true, ...filters }
145
143
 
146
- return this.#mutations.find((mutation) => matchMutation(filters, mutation))
144
+ return this.#mutations.find((mutation) =>
145
+ matchMutation(defaultedFilters, mutation),
146
+ )
147
147
  }
148
148
 
149
149
  findAll(filters: MutationFilters = {}): Mutation[] {
package/src/queryCache.ts CHANGED
@@ -176,13 +176,11 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
176
176
  find<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData>(
177
177
  filters: WithRequired<QueryFilters, 'queryKey'>,
178
178
  ): Query<TQueryFnData, TError, TData> | undefined {
179
- if (typeof filters.exact === 'undefined') {
180
- filters.exact = true
181
- }
179
+ const defaultedFilters = { exact: true, ...filters }
182
180
 
183
- return this.getAll().find((query) => matchQuery(filters, query)) as
184
- | Query<TQueryFnData, TError, TData>
185
- | undefined
181
+ return this.getAll().find((query) =>
182
+ matchQuery(defaultedFilters, query),
183
+ ) as Query<TQueryFnData, TError, TData> | undefined
186
184
  }
187
185
 
188
186
  findAll(filters: QueryFilters = {}): Query[] {
@@ -212,14 +212,12 @@ export class QueryClient {
212
212
  filters: QueryFilters = {},
213
213
  cancelOptions: CancelOptions = {},
214
214
  ): Promise<void> {
215
- if (typeof cancelOptions.revert === 'undefined') {
216
- cancelOptions.revert = true
217
- }
215
+ const defaultedCancelOptions = { revert: true, ...cancelOptions }
218
216
 
219
217
  const promises = notifyManager.batch(() =>
220
218
  this.#queryCache
221
219
  .findAll(filters)
222
- .map((query) => query.cancel(cancelOptions)),
220
+ .map((query) => query.cancel(defaultedCancelOptions)),
223
221
  )
224
222
 
225
223
  return Promise.all(promises).then(noop).catch(noop)
@@ -64,10 +64,12 @@ export class QueryObserver<
64
64
  TQueryData,
65
65
  TQueryKey
66
66
  >
67
- #previousQueryResult?: QueryObserverResult<TData, TError>
68
67
  #selectError: TError | null
69
68
  #selectFn?: (data: TQueryData) => TData
70
69
  #selectResult?: TData
70
+ // This property keeps track of the last defined query data.
71
+ // It will be used to pass the previous data to the placeholder function between renders.
72
+ #lastDefinedQueryData?: TQueryData
71
73
  #staleTimeoutId?: ReturnType<typeof setTimeout>
72
74
  #refetchIntervalId?: ReturnType<typeof setInterval>
73
75
  #currentRefetchInterval?: number | false
@@ -414,9 +416,6 @@ export class QueryObserver<
414
416
  const queryInitialState = queryChange
415
417
  ? query.state
416
418
  : this.#currentQueryInitialState
417
- const prevQueryResult = queryChange
418
- ? this.#currentResult
419
- : this.#previousQueryResult
420
419
 
421
420
  const { state } = query
422
421
  let { error, errorUpdatedAt, fetchStatus, status } = state
@@ -490,7 +489,7 @@ export class QueryObserver<
490
489
  typeof options.placeholderData === 'function'
491
490
  ? (
492
491
  options.placeholderData as unknown as PlaceholderDataFunction<TQueryData>
493
- )(prevQueryResult?.data as TQueryData | undefined)
492
+ )(this.#lastDefinedQueryData)
494
493
  : options.placeholderData
495
494
  if (options.select && typeof placeholderData !== 'undefined') {
496
495
  try {
@@ -504,7 +503,11 @@ export class QueryObserver<
504
503
 
505
504
  if (typeof placeholderData !== 'undefined') {
506
505
  status = 'success'
507
- data = replaceData(prevResult?.data, placeholderData, options) as TData
506
+ data = replaceData(
507
+ prevResult?.data,
508
+ placeholderData as unknown,
509
+ options,
510
+ ) as TData
508
511
  isPlaceholderData = true
509
512
  }
510
513
  }
@@ -568,6 +571,9 @@ export class QueryObserver<
568
571
  return
569
572
  }
570
573
 
574
+ if (this.#currentResultState.data !== undefined) {
575
+ this.#lastDefinedQueryData = this.#currentResultState.data
576
+ }
571
577
  this.#currentResult = nextResult
572
578
 
573
579
  // Determine which callbacks to trigger
@@ -619,7 +625,6 @@ export class QueryObserver<
619
625
  | undefined
620
626
  this.#currentQuery = query
621
627
  this.#currentQueryInitialState = query.state
622
- this.#previousQueryResult = this.#currentResult
623
628
 
624
629
  if (this.hasListeners()) {
625
630
  prevQuery?.removeObserver(this)
@@ -691,6 +691,61 @@ describe('queryObserver', () => {
691
691
  expect(observer.getCurrentResult().isPlaceholderData).toBe(false)
692
692
  })
693
693
 
694
+ test('should pass the correct previous data to placeholderData function params when select function is used in conjunction', async () => {
695
+ const results: QueryObserverResult[] = []
696
+
697
+ const key1 = queryKey()
698
+ const key2 = queryKey()
699
+
700
+ const data1 = { value: 'data1' }
701
+ const data2 = { value: 'data2' }
702
+
703
+ const observer = new QueryObserver(queryClient, {
704
+ queryKey: key1,
705
+ queryFn: () => data1,
706
+ placeholderData: (prev) => prev,
707
+ select: (data) => data.value,
708
+ })
709
+
710
+ const unsubscribe = observer.subscribe((result) => {
711
+ results.push(result)
712
+ })
713
+
714
+ await sleep(1)
715
+
716
+ observer.setOptions({
717
+ queryKey: key2,
718
+ queryFn: () => data2,
719
+ placeholderData: (prev) => prev,
720
+ select: (data) => data.value,
721
+ })
722
+
723
+ await sleep(1)
724
+ unsubscribe()
725
+
726
+ expect(results.length).toBe(4)
727
+ expect(results[0]).toMatchObject({
728
+ data: undefined,
729
+ status: 'pending',
730
+ fetchStatus: 'fetching',
731
+ }) // Initial fetch
732
+ expect(results[1]).toMatchObject({
733
+ data: 'data1',
734
+ status: 'success',
735
+ fetchStatus: 'idle',
736
+ }) // Successful fetch
737
+ expect(results[2]).toMatchObject({
738
+ data: 'data1',
739
+ status: 'success',
740
+ fetchStatus: 'fetching',
741
+ }) // Fetch for new key, but using previous data as placeholder
742
+ expect(results[3]).toMatchObject({
743
+ data: 'data2',
744
+ status: 'success',
745
+ fetchStatus: 'idle',
746
+ }) // Successful fetch for new key
747
+ })
748
+
694
749
  test('setOptions should notify cache listeners', async () => {
695
750
  const key = queryKey()
696
751