@tanstack/solid-query 5.0.0-beta.20 → 5.0.0-beta.23

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/build/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { notifyManager, QueryClient as QueryClient$1, MutationObserver, QueriesObserver, hydrate, QueryObserver, InfiniteQueryObserver } from '@tanstack/query-core';
2
2
  export * from '@tanstack/query-core';
3
- import { batch, createContext, useContext, onMount, onCleanup, createMemo, createSignal, createComputed, on, createResource } from 'solid-js';
3
+ import { batch, createContext, useContext, createRenderEffect, onCleanup, createMemo, createSignal, createComputed, on, mergeProps, createResource, onMount, untrack } from 'solid-js';
4
4
  import { createComponent, isServer } from 'solid-js/web';
5
5
  import { createStore, unwrap, reconcile } from 'solid-js/store';
6
6
 
@@ -20,17 +20,17 @@ var useQueryClient = (queryClient) => {
20
20
  if (!client) {
21
21
  throw new Error("No QueryClient set, use QueryClientProvider to set one");
22
22
  }
23
- return client;
23
+ return client();
24
24
  };
25
25
  var QueryClientProvider = (props) => {
26
- onMount(() => {
26
+ createRenderEffect((unmount) => {
27
+ unmount?.();
27
28
  props.client.mount();
29
+ return props.client.unmount.bind(props.client);
28
30
  });
29
31
  onCleanup(() => props.client.unmount());
30
32
  return createComponent(QueryClientContext.Provider, {
31
- get value() {
32
- return props.client;
33
- },
33
+ value: () => props.client,
34
34
  get children() {
35
35
  return props.children;
36
36
  }
@@ -44,6 +44,9 @@ function shouldThrowError(throwError, params) {
44
44
  }
45
45
  return !!throwError;
46
46
  }
47
+ var IsRestoringContext = createContext(() => false);
48
+ var useIsRestoring = () => useContext(IsRestoringContext);
49
+ var IsRestoringProvider = IsRestoringContext.Provider;
47
50
 
48
51
  // src/createBaseQuery.ts
49
52
  function reconcileFn(store, result, reconcileOption) {
@@ -77,21 +80,31 @@ var hydrateableObserverResult = (query, result) => {
77
80
  };
78
81
  function createBaseQuery(options, Observer, queryClient) {
79
82
  const client = createMemo(() => useQueryClient(queryClient?.()));
80
- const defaultedOptions = client().defaultQueryOptions(options());
81
- defaultedOptions._optimisticResults = "optimistic";
82
- defaultedOptions.structuralSharing = false;
83
- if (isServer) {
84
- defaultedOptions.retry = false;
85
- defaultedOptions.throwOnError = true;
86
- }
87
- const observer = new Observer(client(), defaultedOptions);
83
+ const isRestoring = useIsRestoring();
84
+ const defaultedOptions = createMemo(
85
+ () => mergeProps(client().defaultQueryOptions(options()), {
86
+ get _optimisticResults() {
87
+ return isRestoring() ? "isRestoring" : "optimistic";
88
+ },
89
+ structuralSharing: false,
90
+ ...isServer && { retry: false, throwOnError: true }
91
+ })
92
+ );
93
+ const [observer, setObserver] = createSignal(
94
+ new Observer(client(), untrack(defaultedOptions))
95
+ );
96
+ createComputed(
97
+ on(client, (c) => setObserver(new Observer(c, defaultedOptions())), {
98
+ defer: true
99
+ })
100
+ );
88
101
  const [state, setState] = createStore(
89
- observer.getOptimisticResult(defaultedOptions)
102
+ observer().getOptimisticResult(defaultedOptions())
90
103
  );
91
104
  const createServerSubscriber = (resolve, reject) => {
92
- return observer.subscribe((result) => {
105
+ return observer().subscribe((result) => {
93
106
  notifyManager.batchCalls(() => {
94
- const query = observer.getCurrentQuery();
107
+ const query = observer().getCurrentQuery();
95
108
  const unwrappedResult = hydrateableObserverResult(query, result);
96
109
  if (unwrappedResult.isError) {
97
110
  reject(unwrappedResult.error);
@@ -102,44 +115,36 @@ function createBaseQuery(options, Observer, queryClient) {
102
115
  });
103
116
  };
104
117
  const createClientSubscriber = () => {
105
- return observer.subscribe((result) => {
118
+ const obs = observer();
119
+ return obs.subscribe((result) => {
106
120
  notifyManager.batchCalls(() => {
107
- const reconcileOptions = observer.options.reconcile;
108
- if (queryResource()?.data && result.data && !queryResource.loading) {
109
- setState((store) => {
110
- return reconcileFn(
111
- store,
112
- result,
113
- reconcileOptions === void 0 ? "id" : reconcileOptions
114
- );
115
- });
121
+ const reconcileOptions = obs.options.reconcile;
122
+ setState((store) => {
123
+ return reconcileFn(
124
+ store,
125
+ result,
126
+ reconcileOptions === void 0 ? "id" : reconcileOptions
127
+ );
128
+ });
129
+ if (queryResource()?.data && result.data && !queryResource.loading && isRestoring())
116
130
  mutate(state);
117
- } else {
118
- setState((store) => {
119
- return reconcileFn(
120
- store,
121
- result,
122
- reconcileOptions === void 0 ? "id" : reconcileOptions
123
- );
124
- });
131
+ else
125
132
  refetch();
126
- }
127
133
  })();
128
134
  });
129
135
  };
130
136
  let unsubscribe = null;
131
137
  const [queryResource, { refetch, mutate }] = createResource(
132
138
  () => {
139
+ const obs = observer();
133
140
  return new Promise((resolve, reject) => {
134
- if (isServer) {
141
+ if (isServer)
135
142
  unsubscribe = createServerSubscriber(resolve, reject);
136
- } else {
137
- if (!unsubscribe) {
138
- unsubscribe = createClientSubscriber();
139
- }
140
- }
141
- if (!state.isLoading) {
142
- const query = observer.getCurrentQuery();
143
+ else if (!unsubscribe && !isRestoring())
144
+ unsubscribe = createClientSubscriber();
145
+ obs.updateResult();
146
+ if (!state.isLoading && !isRestoring()) {
147
+ const query = obs.getCurrentQuery();
143
148
  resolve(hydrateableObserverResult(query, state));
144
149
  }
145
150
  });
@@ -147,7 +152,9 @@ function createBaseQuery(options, Observer, queryClient) {
147
152
  {
148
153
  initialValue: state,
149
154
  // If initialData is provided, we resolve the resource immediately
150
- ssrLoadFrom: options().initialData ? "initial" : "server",
155
+ get ssrLoadFrom() {
156
+ return options().initialData ? "initial" : "server";
157
+ },
151
158
  get deferStream() {
152
159
  return options().deferStream;
153
160
  },
@@ -160,29 +167,43 @@ function createBaseQuery(options, Observer, queryClient) {
160
167
  * Note that this is only invoked on the client, for queries that were originally run on the server.
161
168
  */
162
169
  onHydrated(_k, info) {
170
+ const defaultOptions = defaultedOptions();
163
171
  if (info.value) {
164
172
  hydrate(client(), {
165
173
  queries: [
166
174
  {
167
- queryKey: defaultedOptions.queryKey,
168
- queryHash: defaultedOptions.queryHash,
175
+ queryKey: defaultOptions.queryKey,
176
+ queryHash: defaultOptions.queryHash,
169
177
  state: info.value
170
178
  }
171
179
  ]
172
180
  });
173
181
  }
174
- if (!unsubscribe) {
175
- const newOptions = { ...defaultedOptions };
176
- if (defaultedOptions.staleTime || !defaultedOptions.initialData) {
177
- newOptions.refetchOnMount = false;
178
- }
179
- observer.setOptions(newOptions);
180
- setState(observer.getOptimisticResult(newOptions));
181
- unsubscribe = createClientSubscriber();
182
+ if (unsubscribe)
183
+ return;
184
+ const newOptions = { ...defaultOptions };
185
+ if (defaultOptions.staleTime || !defaultOptions.initialData) {
186
+ newOptions.refetchOnMount = false;
182
187
  }
188
+ observer().setOptions(newOptions);
189
+ setState(observer().getOptimisticResult(newOptions));
190
+ unsubscribe = createClientSubscriber();
183
191
  }
184
192
  }
185
193
  );
194
+ createComputed(
195
+ on(
196
+ [isRestoring, observer],
197
+ ([restoring]) => {
198
+ const unsub = unsubscribe;
199
+ queueMicrotask(() => unsub?.());
200
+ unsubscribe = null;
201
+ if (!restoring)
202
+ refetch();
203
+ },
204
+ { defer: true }
205
+ )
206
+ );
186
207
  onCleanup(() => {
187
208
  if (unsubscribe) {
188
209
  unsubscribe();
@@ -191,8 +212,11 @@ function createBaseQuery(options, Observer, queryClient) {
191
212
  });
192
213
  createComputed(
193
214
  on(
194
- () => client().defaultQueryOptions(options()),
195
- () => observer.setOptions(client().defaultQueryOptions(options())),
215
+ [observer, defaultedOptions],
216
+ ([obs, opts]) => {
217
+ obs.setOptions(opts);
218
+ setState(obs.getOptimisticResult(opts));
219
+ },
196
220
  {
197
221
  // Defer because we don't need to trigger on first render
198
222
  // This only cares about changes to options after the observer is created
@@ -204,9 +228,10 @@ function createBaseQuery(options, Observer, queryClient) {
204
228
  on(
205
229
  () => state.status,
206
230
  () => {
207
- if (state.isError && !state.isFetching && shouldThrowError(observer.options.throwOnError, [
231
+ const obs = observer();
232
+ if (state.isError && !state.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
208
233
  state.error,
209
- observer.getCurrentQuery()
234
+ obs.getCurrentQuery()
210
235
  ])) {
211
236
  throw state.error;
212
237
  }
@@ -252,9 +277,9 @@ function createInfiniteQuery(options, queryClient) {
252
277
  );
253
278
  }
254
279
  function createMutation(options, queryClient) {
255
- const client = useQueryClient(queryClient?.());
280
+ const client = createMemo(() => useQueryClient(queryClient?.()));
256
281
  const observer = new MutationObserver(
257
- client,
282
+ client(),
258
283
  options()
259
284
  );
260
285
  const mutate = (variables, mutateOptions) => {
@@ -303,43 +328,114 @@ function useIsMutating(filters, queryClient) {
303
328
  return mutations;
304
329
  }
305
330
  function createQueries(queriesOptions, queryClient) {
306
- const client = useQueryClient(queryClient?.());
307
- const defaultedQueries = queriesOptions().queries.map((options) => {
308
- const defaultedOptions = client.defaultQueryOptions(options);
309
- defaultedOptions._optimisticResults = "optimistic";
310
- return defaultedOptions;
311
- });
331
+ const client = createMemo(() => useQueryClient(queryClient?.()));
332
+ const isRestoring = useIsRestoring();
333
+ const defaultedQueries = createMemo(
334
+ () => queriesOptions().queries.map(
335
+ (options) => mergeProps(client().defaultQueryOptions(options), {
336
+ get _optimisticResults() {
337
+ return isRestoring() ? "isRestoring" : "optimistic";
338
+ }
339
+ })
340
+ )
341
+ );
312
342
  const observer = new QueriesObserver(
313
- client,
314
- defaultedQueries,
343
+ client(),
344
+ defaultedQueries(),
315
345
  queriesOptions().combine ? {
316
346
  combine: queriesOptions().combine
317
347
  } : void 0
318
348
  );
319
349
  const [state, setState] = createStore(
320
- observer.getOptimisticResult(defaultedQueries)[1]()
350
+ observer.getOptimisticResult(defaultedQueries())[1]()
321
351
  );
322
- const unsubscribe = observer.subscribe((result) => {
323
- notifyManager.batchCalls(() => {
324
- setState(unwrap(result));
325
- })();
352
+ createRenderEffect(
353
+ on(
354
+ () => queriesOptions().queries.length,
355
+ () => setState(observer.getOptimisticResult(defaultedQueries())[1]())
356
+ )
357
+ );
358
+ const dataResources = createMemo(
359
+ on(
360
+ () => state.length,
361
+ () => state.map((queryRes) => {
362
+ const dataPromise = () => new Promise((resolve) => {
363
+ if (queryRes.isFetching && queryRes.isLoading)
364
+ return;
365
+ resolve(unwrap(queryRes.data));
366
+ });
367
+ return createResource(dataPromise);
368
+ })
369
+ )
370
+ );
371
+ batch(() => {
372
+ const dataResources_ = dataResources();
373
+ for (let index = 0; index < dataResources_.length; index++) {
374
+ const dataResource = dataResources_[index];
375
+ dataResource[1].mutate(() => unwrap(state[index].data));
376
+ dataResource[1].refetch();
377
+ }
378
+ });
379
+ let taskQueue = [];
380
+ const subscribeToObserver = () => observer.subscribe((result) => {
381
+ taskQueue.push(() => {
382
+ batch(() => {
383
+ const dataResources_ = dataResources();
384
+ for (let index = 0; index < dataResources_.length; index++) {
385
+ const dataResource = dataResources_[index];
386
+ const unwrappedResult = { ...unwrap(result[index]) };
387
+ setState(index, unwrap(unwrappedResult));
388
+ dataResource[1].mutate(() => unwrap(state[index].data));
389
+ dataResource[1].refetch();
390
+ }
391
+ });
392
+ });
393
+ queueMicrotask(() => {
394
+ const taskToRun = taskQueue.pop();
395
+ if (taskToRun)
396
+ taskToRun();
397
+ taskQueue = [];
398
+ });
399
+ });
400
+ let unsubscribe = () => void 0;
401
+ createComputed((cleanup) => {
402
+ cleanup?.();
403
+ unsubscribe = isRestoring() ? () => void 0 : subscribeToObserver();
404
+ return () => queueMicrotask(unsubscribe);
326
405
  });
327
406
  onCleanup(unsubscribe);
407
+ onMount(() => {
408
+ observer.setQueries(
409
+ defaultedQueries(),
410
+ queriesOptions().combine ? {
411
+ combine: queriesOptions().combine
412
+ } : void 0,
413
+ { listeners: false }
414
+ );
415
+ });
328
416
  createComputed(() => {
329
- const updatedQueries = queriesOptions().queries.map((options) => {
330
- const defaultedOptions = client.defaultQueryOptions(options);
331
- defaultedOptions._optimisticResults = "optimistic";
332
- return defaultedOptions;
333
- });
334
417
  observer.setQueries(
335
- updatedQueries,
418
+ defaultedQueries(),
336
419
  queriesOptions().combine ? {
337
420
  combine: queriesOptions().combine
338
421
  } : void 0,
339
422
  { listeners: false }
340
423
  );
341
424
  });
342
- return state;
425
+ const handler = (index) => ({
426
+ get(target, prop) {
427
+ if (prop === "data") {
428
+ return dataResources()[index][0]();
429
+ }
430
+ return Reflect.get(target, prop);
431
+ }
432
+ });
433
+ const getProxies = () => state.map((s, index) => {
434
+ return new Proxy(s, handler(index));
435
+ });
436
+ const [proxifiedState, setProxifiedState] = createStore(getProxies());
437
+ createRenderEffect(() => setProxifiedState(getProxies()));
438
+ return proxifiedState;
343
439
  }
344
440
 
345
- export { QueryClient, QueryClientContext, QueryClientProvider, createInfiniteQuery, createMutation, createQueries, createQuery, queryOptions, useIsFetching, useIsMutating, useQueryClient };
441
+ export { IsRestoringProvider, QueryClient, QueryClientContext, QueryClientProvider, createInfiniteQuery, createMutation, createQueries, createQuery, queryOptions, useIsFetching, useIsMutating, useIsRestoring, useQueryClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/solid-query",
3
- "version": "5.0.0-beta.20",
3
+ "version": "5.0.0-beta.23",
4
4
  "description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  ],
49
49
  "dependencies": {
50
50
  "solid-js": "^1.7.8",
51
- "@tanstack/query-core": "5.0.0-beta.20"
51
+ "@tanstack/query-core": "5.0.0-beta.23"
52
52
  },
53
53
  "devDependencies": {
54
54
  "tsup-preset-solid": "^2.0.1",
@@ -1,10 +1,15 @@
1
- import { createContext, onCleanup, onMount, useContext } from 'solid-js'
1
+ import {
2
+ createContext,
3
+ createRenderEffect,
4
+ onCleanup,
5
+ useContext,
6
+ } from 'solid-js'
2
7
  import type { QueryClient } from './QueryClient'
3
8
  import type { JSX } from 'solid-js'
4
9
 
5
- export const QueryClientContext = createContext<QueryClient | undefined>(
6
- undefined,
7
- )
10
+ export const QueryClientContext = createContext<
11
+ (() => QueryClient) | undefined
12
+ >(undefined)
8
13
 
9
14
  export const useQueryClient = (queryClient?: QueryClient) => {
10
15
  if (queryClient) {
@@ -16,7 +21,7 @@ export const useQueryClient = (queryClient?: QueryClient) => {
16
21
  throw new Error('No QueryClient set, use QueryClientProvider to set one')
17
22
  }
18
23
 
19
- return client
24
+ return client()
20
25
  }
21
26
 
22
27
  export type QueryClientProviderProps = {
@@ -27,13 +32,15 @@ export type QueryClientProviderProps = {
27
32
  export const QueryClientProvider = (
28
33
  props: QueryClientProviderProps,
29
34
  ): JSX.Element => {
30
- onMount(() => {
35
+ createRenderEffect<() => void>((unmount) => {
36
+ unmount?.()
31
37
  props.client.mount()
38
+ return props.client.unmount.bind(props.client)
32
39
  })
33
40
  onCleanup(() => props.client.unmount())
34
41
 
35
42
  return (
36
- <QueryClientContext.Provider value={props.client}>
43
+ <QueryClientContext.Provider value={() => props.client}>
37
44
  {props.children}
38
45
  </QueryClientContext.Provider>
39
46
  )
@@ -54,7 +54,7 @@ describe('useQueries', () => {
54
54
  }))
55
55
 
56
56
  createRenderEffect(() => {
57
- results.push([...result])
57
+ results.push([{ ...result[0] }, { ...result[1] }])
58
58
  })
59
59
 
60
60
  return (
@@ -898,7 +898,7 @@ describe('createQuery', () => {
898
898
 
899
899
  it('should throw an error when a selector throws', async () => {
900
900
  const key = queryKey()
901
- const states: Array<CreateQueryResult<string>> = []
901
+ const states: Array<{ status: string; data?: unknown; error?: Error }> = []
902
902
  const error = new Error('Select Error')
903
903
 
904
904
  function Page() {
@@ -910,7 +910,10 @@ describe('createQuery', () => {
910
910
  },
911
911
  }))
912
912
  createRenderEffect(() => {
913
- states.push({ ...state })
913
+ if (state.status === 'pending')
914
+ states.push({ status: 'pending', data: undefined })
915
+ else if (state.status === 'error')
916
+ states.push({ status: 'error', error: state.error })
914
917
  })
915
918
  return null
916
919
  }
@@ -4666,11 +4669,10 @@ describe('createQuery', () => {
4666
4669
  })
4667
4670
  })
4668
4671
 
4669
- it('should only call the query hash function once each render', async () => {
4672
+ it('should only call the query hash function once', async () => {
4670
4673
  const key = queryKey()
4671
4674
 
4672
4675
  let hashes = 0
4673
- let renders = 0
4674
4676
 
4675
4677
  function queryKeyHashFn(x: any) {
4676
4678
  hashes++
@@ -4678,20 +4680,12 @@ describe('createQuery', () => {
4678
4680
  }
4679
4681
 
4680
4682
  function Page() {
4681
- const state = createQuery(() => ({
4683
+ createQuery(() => ({
4682
4684
  queryKey: key,
4683
4685
  queryFn: () => 'test',
4684
4686
  queryKeyHashFn,
4685
4687
  }))
4686
4688
 
4687
- createEffect(
4688
- on(
4689
- () => state.status,
4690
- () => {
4691
- renders++
4692
- },
4693
- ),
4694
- )
4695
4689
  return null
4696
4690
  }
4697
4691
 
@@ -4702,8 +4696,7 @@ describe('createQuery', () => {
4702
4696
  ))
4703
4697
 
4704
4698
  await sleep(10)
4705
-
4706
- expect(renders).toBe(hashes)
4699
+ expect(hashes).toBe(1)
4707
4700
  })
4708
4701
 
4709
4702
  it('should refetch when changed enabled to true in error state', async () => {
@@ -4,7 +4,7 @@ import { Show, Suspense, createSignal, startTransition } from 'solid-js'
4
4
  import { QueryCache, QueryClientProvider, createQuery } from '..'
5
5
  import { createQueryClient, queryKey, sleep } from './utils'
6
6
 
7
- describe("useQuery's in Suspense mode with transitions", () => {
7
+ describe("createQuery's in Suspense mode with transitions", () => {
8
8
  const queryCache = new QueryCache()
9
9
  const queryClient = createQueryClient({ queryCache })
10
10
 
@@ -19,7 +19,6 @@ describe("useQuery's in Suspense mode with transitions", () => {
19
19
  return true
20
20
  },
21
21
  }))
22
-
23
22
  return <Show when={state.data}>Message</Show>
24
23
  }
25
24
 
@@ -7,7 +7,7 @@ import type { ParentProps } from 'solid-js'
7
7
  import type { SpyInstance } from 'vitest'
8
8
 
9
9
  let queryKeyCount = 0
10
- export function queryKey(): Array<string> {
10
+ export function queryKey() {
11
11
  queryKeyCount++
12
12
  return [`query_${queryKeyCount}`]
13
13
  }