@tanstack/solid-query 4.11.0 → 4.12.0

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.
Files changed (37) hide show
  1. package/build/lib/__tests__/QueryClientProvider.test.d.ts +1 -0
  2. package/build/lib/__tests__/createInfiniteQuery.test.d.ts +1 -0
  3. package/build/lib/__tests__/createMutation.test.d.ts +1 -0
  4. package/build/lib/__tests__/createQueries.test.d.ts +1 -0
  5. package/build/lib/__tests__/createQuery.test.d.ts +1 -0
  6. package/build/lib/__tests__/createQuery.types.test.d.ts +2 -0
  7. package/build/lib/__tests__/suspense.test.d.ts +1 -0
  8. package/build/lib/__tests__/transition.test.d.ts +1 -0
  9. package/build/lib/__tests__/useIsFetching.test.d.ts +1 -0
  10. package/build/lib/__tests__/useIsMutating.test.d.ts +1 -0
  11. package/build/lib/__tests__/utils.d.ts +27 -0
  12. package/build/lib/createBaseQuery.esm.js +18 -2
  13. package/build/lib/createBaseQuery.esm.js.map +1 -1
  14. package/build/lib/createBaseQuery.js +18 -2
  15. package/build/lib/createBaseQuery.js.map +1 -1
  16. package/build/lib/createBaseQuery.mjs +18 -2
  17. package/build/lib/createBaseQuery.mjs.map +1 -1
  18. package/build/solid/__tests__/QueryClientProvider.test.jsx +185 -0
  19. package/build/solid/__tests__/createInfiniteQuery.test.jsx +1440 -0
  20. package/build/solid/__tests__/createMutation.test.jsx +810 -0
  21. package/build/solid/__tests__/createQueries.test.jsx +958 -0
  22. package/build/solid/__tests__/createQuery.test.jsx +4553 -0
  23. package/build/solid/__tests__/createQuery.types.test.jsx +124 -0
  24. package/build/solid/__tests__/suspense.test.jsx +691 -0
  25. package/build/solid/__tests__/transition.test.jsx +39 -0
  26. package/build/solid/__tests__/useIsFetching.test.jsx +209 -0
  27. package/build/solid/__tests__/useIsMutating.test.jsx +216 -0
  28. package/build/solid/__tests__/utils.jsx +55 -0
  29. package/build/solid/createBaseQuery.js +14 -2
  30. package/build/umd/index.development.js +21 -5
  31. package/build/umd/index.development.js.map +1 -1
  32. package/build/umd/index.production.js +1 -1
  33. package/build/umd/index.production.js.map +1 -1
  34. package/package.json +5 -6
  35. package/src/__tests__/createQuery.test.tsx +15 -84
  36. package/src/__tests__/suspense.test.tsx +0 -46
  37. package/src/createBaseQuery.ts +14 -3
@@ -0,0 +1,39 @@
1
+ import { fireEvent, render, screen, waitFor } from 'solid-testing-library';
2
+ import { createSignal, Show, startTransition, Suspense } from 'solid-js';
3
+ import { createQuery, QueryCache, QueryClientProvider } from '..';
4
+ import { createQueryClient, queryKey, sleep } from './utils';
5
+ describe("useQuery's in Suspense mode with transitions", () => {
6
+ const queryCache = new QueryCache();
7
+ const queryClient = createQueryClient({ queryCache });
8
+ it('should render the content when the transition is done', async () => {
9
+ const key = queryKey();
10
+ function Suspended() {
11
+ const state = createQuery(key, async () => {
12
+ await sleep(10);
13
+ return true;
14
+ });
15
+ return <Show when={state.data}>Message</Show>;
16
+ }
17
+ function Page() {
18
+ const [showSignal, setShowSignal] = createSignal(false);
19
+ return (<div>
20
+ <button aria-label="toggle" onClick={() => startTransition(() => setShowSignal((value) => !value))}>
21
+ {showSignal() ? 'Hide' : 'Show'}
22
+ </button>
23
+ <Suspense fallback="Loading">
24
+ <Show when={showSignal()}>
25
+ <Suspended />
26
+ </Show>
27
+ </Suspense>
28
+ </div>);
29
+ }
30
+ render(() => (<QueryClientProvider client={queryClient}>
31
+ <Page />
32
+ </QueryClientProvider>));
33
+ await waitFor(() => screen.getByText('Show'));
34
+ fireEvent.click(screen.getByLabelText('toggle'));
35
+ await waitFor(() => screen.getByText('Message'));
36
+ // verify that the button also updated. See https://github.com/solidjs/solid/issues/1249
37
+ await waitFor(() => screen.getByText('Hide'));
38
+ });
39
+ });
@@ -0,0 +1,209 @@
1
+ import { fireEvent, render, screen, waitFor } from 'solid-testing-library';
2
+ import { createContext, createEffect, createRenderEffect, createSignal, ErrorBoundary, Show, } from 'solid-js';
3
+ import { createQuery, QueryCache, QueryClientProvider, useIsFetching } from '..';
4
+ import { createQueryClient, queryKey, setActTimeout, sleep } from './utils';
5
+ describe('useIsFetching', () => {
6
+ // See https://github.com/tannerlinsley/react-query/issues/105
7
+ it('should update as queries start and stop fetching', async () => {
8
+ const queryCache = new QueryCache();
9
+ const queryClient = createQueryClient({ queryCache });
10
+ const key = queryKey();
11
+ function IsFetching() {
12
+ const isFetching = useIsFetching();
13
+ return <div>isFetching: {isFetching()}</div>;
14
+ }
15
+ function Query() {
16
+ const [ready, setReady] = createSignal(false);
17
+ createQuery(key, async () => {
18
+ await sleep(50);
19
+ return 'test';
20
+ }, {
21
+ get enabled() {
22
+ return ready();
23
+ },
24
+ });
25
+ return <button onClick={() => setReady(true)}>setReady</button>;
26
+ }
27
+ function Page() {
28
+ return (<div>
29
+ <IsFetching />
30
+ <Query />
31
+ </div>);
32
+ }
33
+ render(() => (<QueryClientProvider client={queryClient}>
34
+ <Page />
35
+ </QueryClientProvider>));
36
+ await screen.findByText('isFetching: 0');
37
+ fireEvent.click(screen.getByRole('button', { name: /setReady/i }));
38
+ await screen.findByText('isFetching: 1');
39
+ await screen.findByText('isFetching: 0');
40
+ });
41
+ it('should not update state while rendering', async () => {
42
+ const queryCache = new QueryCache();
43
+ const queryClient = createQueryClient({ queryCache });
44
+ const key1 = queryKey();
45
+ const key2 = queryKey();
46
+ const isFetchings = [];
47
+ function IsFetching() {
48
+ const isFetching = useIsFetching();
49
+ createRenderEffect(() => {
50
+ isFetchings.push(isFetching());
51
+ });
52
+ return null;
53
+ }
54
+ function FirstQuery() {
55
+ createQuery(key1, async () => {
56
+ await sleep(100);
57
+ return 'data';
58
+ });
59
+ return null;
60
+ }
61
+ function SecondQuery() {
62
+ createQuery(key2, async () => {
63
+ await sleep(100);
64
+ return 'data';
65
+ });
66
+ return null;
67
+ }
68
+ function Page() {
69
+ const [renderSecond, setRenderSecond] = createSignal(false);
70
+ createEffect(() => {
71
+ setActTimeout(() => {
72
+ setRenderSecond(true);
73
+ }, 50);
74
+ });
75
+ return (<>
76
+ <IsFetching />
77
+ <FirstQuery />
78
+ <Show when={renderSecond()}>
79
+ <SecondQuery />
80
+ </Show>
81
+ </>);
82
+ }
83
+ render(() => (<QueryClientProvider client={queryClient}>
84
+ <Page />
85
+ </QueryClientProvider>));
86
+ // unlike react, Updating renderSecond wont cause a rerender for FirstQuery
87
+ await waitFor(() => expect(isFetchings).toEqual([0, 1, 2, 1, 0]));
88
+ });
89
+ it('should be able to filter', async () => {
90
+ const queryClient = createQueryClient();
91
+ const key1 = queryKey();
92
+ const key2 = queryKey();
93
+ const isFetchings = [];
94
+ function One() {
95
+ createQuery(key1, async () => {
96
+ await sleep(10);
97
+ return 'test';
98
+ });
99
+ return null;
100
+ }
101
+ function Two() {
102
+ createQuery(key2, async () => {
103
+ await sleep(20);
104
+ return 'test';
105
+ });
106
+ return null;
107
+ }
108
+ function Page() {
109
+ const [started, setStarted] = createSignal(false);
110
+ const isFetching = useIsFetching(key1);
111
+ createRenderEffect(() => {
112
+ isFetchings.push(isFetching());
113
+ });
114
+ return (<div>
115
+ <button onClick={() => setStarted(true)}>setStarted</button>
116
+ <div>isFetching: {isFetching()}</div>
117
+ <Show when={started()}>
118
+ <>
119
+ <One />
120
+ <Two />
121
+ </>
122
+ </Show>
123
+ </div>);
124
+ }
125
+ render(() => (<QueryClientProvider client={queryClient}>
126
+ <Page />
127
+ </QueryClientProvider>));
128
+ await screen.findByText('isFetching: 0');
129
+ fireEvent.click(screen.getByRole('button', { name: /setStarted/i }));
130
+ await screen.findByText('isFetching: 1');
131
+ await screen.findByText('isFetching: 0');
132
+ // at no point should we have isFetching: 2
133
+ expect(isFetchings).toEqual(expect.not.arrayContaining([2]));
134
+ });
135
+ describe('with custom context', () => {
136
+ it('should update as queries start and stop fetching', async () => {
137
+ const context = createContext(undefined);
138
+ const queryCache = new QueryCache();
139
+ const queryClient = createQueryClient({ queryCache });
140
+ const key = queryKey();
141
+ function Page() {
142
+ const [ready, setReady] = createSignal(false);
143
+ const isFetching = useIsFetching(undefined, { context: context });
144
+ createQuery(key, async () => {
145
+ await sleep(50);
146
+ return 'test';
147
+ }, {
148
+ get enabled() {
149
+ return ready();
150
+ },
151
+ context,
152
+ });
153
+ return (<div>
154
+ <div>isFetching: {isFetching}</div>
155
+ <button onClick={() => setReady(true)}>setReady</button>
156
+ </div>);
157
+ }
158
+ render(() => (<QueryClientProvider client={queryClient} context={context}>
159
+ <Page />
160
+ </QueryClientProvider>));
161
+ await screen.findByText('isFetching: 0');
162
+ fireEvent.click(screen.getByRole('button', { name: /setReady/i }));
163
+ await screen.findByText('isFetching: 1');
164
+ await screen.findByText('isFetching: 0');
165
+ });
166
+ it('should throw if the context is not passed to useIsFetching', async () => {
167
+ const context = createContext(undefined);
168
+ const queryCache = new QueryCache();
169
+ const queryClient = createQueryClient({ queryCache });
170
+ const key = queryKey();
171
+ function Page() {
172
+ const isFetching = useIsFetching();
173
+ createQuery(key, async () => 'test', {
174
+ enabled: true,
175
+ context,
176
+ useErrorBoundary: true,
177
+ });
178
+ return (<div>
179
+ <div>isFetching: {isFetching}</div>
180
+ </div>);
181
+ }
182
+ render(() => (<QueryClientProvider client={queryClient} context={context}>
183
+ <ErrorBoundary fallback={() => <div>error boundary</div>}>
184
+ <Page />
185
+ </ErrorBoundary>
186
+ </QueryClientProvider>));
187
+ await waitFor(() => screen.getByText('error boundary'));
188
+ });
189
+ });
190
+ it('should show the correct fetching state when mounted after a query', async () => {
191
+ const queryClient = createQueryClient();
192
+ const key = queryKey();
193
+ function Page() {
194
+ createQuery(key, async () => {
195
+ await sleep(10);
196
+ return 'test';
197
+ });
198
+ const isFetching = useIsFetching();
199
+ return (<div>
200
+ <div>isFetching: {isFetching()}</div>
201
+ </div>);
202
+ }
203
+ render(() => (<QueryClientProvider client={queryClient}>
204
+ <Page />
205
+ </QueryClientProvider>));
206
+ await screen.findByText('isFetching: 1');
207
+ await screen.findByText('isFetching: 0');
208
+ });
209
+ });
@@ -0,0 +1,216 @@
1
+ import { fireEvent, screen, waitFor } from 'solid-testing-library';
2
+ import { createMutation, QueryClient, QueryClientProvider, useIsMutating, } from '..';
3
+ import { createQueryClient, sleep } from './utils';
4
+ import { createContext, createEffect, createRenderEffect, createSignal, ErrorBoundary, Show, } from 'solid-js';
5
+ import { render } from 'solid-testing-library';
6
+ import * as MutationCacheModule from '../../../query-core/src/mutationCache';
7
+ import { setActTimeout } from './utils';
8
+ describe('useIsMutating', () => {
9
+ it('should return the number of fetching mutations', async () => {
10
+ const isMutatings = [];
11
+ const queryClient = createQueryClient();
12
+ function IsMutating() {
13
+ const isMutating = useIsMutating();
14
+ createRenderEffect(() => {
15
+ isMutatings.push(isMutating());
16
+ });
17
+ return null;
18
+ }
19
+ function Mutations() {
20
+ const { mutate: mutate1 } = createMutation(['mutation1'], async () => {
21
+ await sleep(150);
22
+ return 'data';
23
+ });
24
+ const { mutate: mutate2 } = createMutation(['mutation2'], async () => {
25
+ await sleep(50);
26
+ return 'data';
27
+ });
28
+ createEffect(() => {
29
+ mutate1();
30
+ setActTimeout(() => {
31
+ mutate2();
32
+ }, 50);
33
+ });
34
+ return null;
35
+ }
36
+ function Page() {
37
+ return (<div>
38
+ <IsMutating />
39
+ <Mutations />
40
+ </div>);
41
+ }
42
+ render(() => (<QueryClientProvider client={queryClient}>
43
+ <Page />
44
+ </QueryClientProvider>));
45
+ await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0]));
46
+ });
47
+ it('should filter correctly by mutationKey', async () => {
48
+ const isMutatings = [];
49
+ const queryClient = createQueryClient();
50
+ function IsMutating() {
51
+ const isMutating = useIsMutating(['mutation1']);
52
+ createRenderEffect(() => {
53
+ isMutatings.push(isMutating());
54
+ });
55
+ return null;
56
+ }
57
+ function Page() {
58
+ const { mutate: mutate1 } = createMutation(['mutation1'], async () => {
59
+ await sleep(100);
60
+ return 'data';
61
+ });
62
+ const { mutate: mutate2 } = createMutation(['mutation2'], async () => {
63
+ await sleep(100);
64
+ return 'data';
65
+ });
66
+ createEffect(() => {
67
+ mutate1();
68
+ mutate2();
69
+ });
70
+ return <IsMutating />;
71
+ }
72
+ render(() => (<QueryClientProvider client={queryClient}>
73
+ <Page />
74
+ </QueryClientProvider>));
75
+ // Unlike React, IsMutating Wont re-render twice with mutation2
76
+ await waitFor(() => expect(isMutatings).toEqual([0, 1, 0]));
77
+ });
78
+ it('should filter correctly by predicate', async () => {
79
+ const isMutatings = [];
80
+ const queryClient = createQueryClient();
81
+ function IsMutating() {
82
+ const isMutating = useIsMutating({
83
+ predicate: (mutation) => mutation.options.mutationKey?.[0] === 'mutation1',
84
+ });
85
+ createRenderEffect(() => {
86
+ isMutatings.push(isMutating());
87
+ });
88
+ return null;
89
+ }
90
+ function Page() {
91
+ const { mutate: mutate1 } = createMutation(['mutation1'], async () => {
92
+ await sleep(100);
93
+ return 'data';
94
+ });
95
+ const { mutate: mutate2 } = createMutation(['mutation2'], async () => {
96
+ await sleep(100);
97
+ return 'data';
98
+ });
99
+ createEffect(() => {
100
+ mutate1();
101
+ mutate2();
102
+ });
103
+ return <IsMutating />;
104
+ }
105
+ render(() => (<QueryClientProvider client={queryClient}>
106
+ <Page />
107
+ </QueryClientProvider>));
108
+ // Again, No unnecessary re-renders like React
109
+ await waitFor(() => expect(isMutatings).toEqual([0, 1, 0]));
110
+ });
111
+ it('should not change state if unmounted', async () => {
112
+ // We have to mock the MutationCache to not unsubscribe
113
+ // the listener when the component is unmounted
114
+ class MutationCacheMock extends MutationCacheModule.MutationCache {
115
+ subscribe(listener) {
116
+ super.subscribe(listener);
117
+ return () => void 0;
118
+ }
119
+ }
120
+ const MutationCacheSpy = jest
121
+ .spyOn(MutationCacheModule, 'MutationCache')
122
+ .mockImplementation((fn) => {
123
+ return new MutationCacheMock(fn);
124
+ });
125
+ const queryClient = createQueryClient();
126
+ function IsMutating() {
127
+ useIsMutating();
128
+ return null;
129
+ }
130
+ function Page() {
131
+ const [mounted, setMounted] = createSignal(true);
132
+ const { mutate: mutate1 } = createMutation(['mutation1'], async () => {
133
+ await sleep(10);
134
+ return 'data';
135
+ });
136
+ createEffect(() => {
137
+ mutate1();
138
+ });
139
+ return (<div>
140
+ <button onClick={() => setMounted(false)}>unmount</button>
141
+ <Show when={mounted()}>
142
+ <IsMutating />
143
+ </Show>
144
+ </div>);
145
+ }
146
+ render(() => (<QueryClientProvider client={queryClient}>
147
+ <Page />
148
+ </QueryClientProvider>));
149
+ fireEvent.click(screen.getByText('unmount'));
150
+ // Should not display the console error
151
+ // "Warning: Can't perform a React state update on an unmounted component"
152
+ await sleep(20);
153
+ MutationCacheSpy.mockRestore();
154
+ });
155
+ describe('with custom context', () => {
156
+ it('should return the number of fetching mutations', async () => {
157
+ const context = createContext(undefined);
158
+ const isMutatings = [];
159
+ const queryClient = new QueryClient();
160
+ function IsMutating() {
161
+ const isMutating = useIsMutating(undefined, { context });
162
+ createRenderEffect(() => {
163
+ isMutatings.push(isMutating());
164
+ });
165
+ return null;
166
+ }
167
+ function Page() {
168
+ const { mutate: mutate1 } = createMutation(['mutation1'], async () => {
169
+ await sleep(150);
170
+ return 'data';
171
+ }, { context });
172
+ const { mutate: mutate2 } = createMutation(['mutation2'], async () => {
173
+ await sleep(50);
174
+ return 'data';
175
+ }, { context });
176
+ createEffect(() => {
177
+ mutate1();
178
+ setActTimeout(() => {
179
+ mutate2();
180
+ }, 50);
181
+ });
182
+ return <IsMutating />;
183
+ }
184
+ render(() => (<QueryClientProvider client={queryClient} context={context}>
185
+ <Page />
186
+ </QueryClientProvider>));
187
+ await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0]));
188
+ });
189
+ it('should throw if the context is not passed to useIsMutating', async () => {
190
+ const context = createContext(undefined);
191
+ const isMutatings = [];
192
+ const queryClient = new QueryClient();
193
+ function IsMutating() {
194
+ const isMutating = useIsMutating(undefined);
195
+ isMutatings.push(isMutating());
196
+ return null;
197
+ }
198
+ function Page() {
199
+ const { mutate } = createMutation(['mutation'], async () => 'data', {
200
+ useErrorBoundary: true,
201
+ context,
202
+ });
203
+ createEffect(() => {
204
+ mutate();
205
+ });
206
+ return <IsMutating />;
207
+ }
208
+ render(() => (<QueryClientProvider client={queryClient} context={context}>
209
+ <ErrorBoundary fallback={() => <div>error boundary</div>}>
210
+ <Page />
211
+ </ErrorBoundary>
212
+ </QueryClientProvider>));
213
+ await waitFor(() => screen.getByText('error boundary'));
214
+ });
215
+ });
216
+ });
@@ -0,0 +1,55 @@
1
+ import { QueryClient } from '@tanstack/query-core';
2
+ import { createEffect, createSignal, onCleanup, Show } from 'solid-js';
3
+ let queryKeyCount = 0;
4
+ export function queryKey() {
5
+ const localQueryKeyCount = queryKeyCount++;
6
+ return () => [`query_${localQueryKeyCount}`];
7
+ }
8
+ export const Blink = (props) => {
9
+ const [shouldShow, setShouldShow] = createSignal(true);
10
+ createEffect(() => {
11
+ setShouldShow(true);
12
+ const timeout = setActTimeout(() => setShouldShow(false), props.duration);
13
+ onCleanup(() => clearTimeout(timeout));
14
+ });
15
+ return (<Show when={shouldShow()} fallback={<>off</>}>
16
+ <>{props.children}</>
17
+ </Show>);
18
+ };
19
+ export function createQueryClient(config) {
20
+ jest.spyOn(console, 'error').mockImplementation(() => undefined);
21
+ return new QueryClient({ logger: mockLogger, ...config });
22
+ }
23
+ export function mockVisibilityState(value) {
24
+ return jest.spyOn(document, 'visibilityState', 'get').mockReturnValue(value);
25
+ }
26
+ export function mockNavigatorOnLine(value) {
27
+ return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value);
28
+ }
29
+ export const mockLogger = {
30
+ log: jest.fn(),
31
+ warn: jest.fn(),
32
+ error: jest.fn(),
33
+ };
34
+ export function sleep(timeout) {
35
+ return new Promise((resolve, _reject) => {
36
+ setTimeout(resolve, timeout);
37
+ });
38
+ }
39
+ export function setActTimeout(fn, ms) {
40
+ return setTimeout(() => {
41
+ fn();
42
+ }, ms);
43
+ }
44
+ /**
45
+ * Assert the parameter is of a specific type.
46
+ */
47
+ export function expectType(_) {
48
+ return undefined;
49
+ }
50
+ /**
51
+ * Assert the parameter is not typed as `any`
52
+ */
53
+ export function expectTypeNotAny(_) {
54
+ return undefined;
55
+ }
@@ -5,6 +5,7 @@ import { shouldThrowError } from './utils';
5
5
  // Base Query Function that is used to create the query.
6
6
  export function createBaseQuery(options, Observer) {
7
7
  const queryClient = useQueryClient({ context: options.context });
8
+ const emptyData = Symbol('empty');
8
9
  const defaultedOptions = queryClient.defaultQueryOptions(options);
9
10
  defaultedOptions._optimisticResults = 'optimistic';
10
11
  const observer = new Observer(queryClient, defaultedOptions);
@@ -14,6 +15,9 @@ export function createBaseQuery(options, Observer) {
14
15
  const [dataResource, { refetch, mutate }] = createResource(() => {
15
16
  return new Promise((resolve) => {
16
17
  if (!(state.isFetching && state.isLoading)) {
18
+ if (unwrap(state.data) === emptyData) {
19
+ resolve(undefined);
20
+ }
17
21
  resolve(unwrap(state.data));
18
22
  }
19
23
  });
@@ -26,7 +30,15 @@ export function createBaseQuery(options, Observer) {
26
30
  const unsubscribe = observer.subscribe((result) => {
27
31
  taskQueue.push(() => {
28
32
  batch(() => {
29
- setState(unwrap(result));
33
+ const unwrappedResult = { ...unwrap(result) };
34
+ if (unwrappedResult.data === undefined) {
35
+ // This is a hack to prevent Solid
36
+ // from deleting the data property when it is `undefined`
37
+ // ref: https://www.solidjs.com/docs/latest/api#updating-stores
38
+ // @ts-ignore
39
+ unwrappedResult.data = emptyData;
40
+ }
41
+ setState(unwrap(unwrappedResult));
30
42
  mutate(() => unwrap(result.data));
31
43
  refetch();
32
44
  });
@@ -59,7 +71,7 @@ export function createBaseQuery(options, Observer) {
59
71
  }));
60
72
  const handler = {
61
73
  get(target, prop) {
62
- if (prop === 'data' && target.isLoading && target.isFetching) {
74
+ if (prop === 'data') {
63
75
  return dataResource();
64
76
  }
65
77
  return Reflect.get(target, prop);
@@ -1476,7 +1476,7 @@
1476
1476
  variables: this.options.variables
1477
1477
  }); // Notify cache callback
1478
1478
 
1479
- (_this$mutationCache$c = (_this$mutationCache$c2 = this.mutationCache.config).onMutate) == null ? void 0 : _this$mutationCache$c.call(_this$mutationCache$c2, this.state.variables, this);
1479
+ await ((_this$mutationCache$c = (_this$mutationCache$c2 = this.mutationCache.config).onMutate) == null ? void 0 : _this$mutationCache$c.call(_this$mutationCache$c2, this.state.variables, this));
1480
1480
  const context = await ((_this$options$onMutat = (_this$options = this.options).onMutate) == null ? void 0 : _this$options$onMutat.call(_this$options, this.state.variables));
1481
1481
 
1482
1482
  if (context !== this.state.context) {
@@ -1490,7 +1490,7 @@
1490
1490
 
1491
1491
  const data = await executeMutation(); // Notify cache callback
1492
1492
 
1493
- (_this$mutationCache$c3 = (_this$mutationCache$c4 = this.mutationCache.config).onSuccess) == null ? void 0 : _this$mutationCache$c3.call(_this$mutationCache$c4, data, this.state.variables, this.state.context, this);
1493
+ await ((_this$mutationCache$c3 = (_this$mutationCache$c4 = this.mutationCache.config).onSuccess) == null ? void 0 : _this$mutationCache$c3.call(_this$mutationCache$c4, data, this.state.variables, this.state.context, this));
1494
1494
  await ((_this$options$onSucce = (_this$options2 = this.options).onSuccess) == null ? void 0 : _this$options$onSucce.call(_this$options2, data, this.state.variables, this.state.context));
1495
1495
  await ((_this$options$onSettl = (_this$options3 = this.options).onSettled) == null ? void 0 : _this$options$onSettl.call(_this$options3, data, null, this.state.variables, this.state.context));
1496
1496
  this.dispatch({
@@ -1503,7 +1503,7 @@
1503
1503
  var _this$mutationCache$c5, _this$mutationCache$c6, _this$options$onError, _this$options4, _this$options$onSettl2, _this$options5;
1504
1504
 
1505
1505
  // Notify cache callback
1506
- (_this$mutationCache$c5 = (_this$mutationCache$c6 = this.mutationCache.config).onError) == null ? void 0 : _this$mutationCache$c5.call(_this$mutationCache$c6, error, this.state.variables, this.state.context, this);
1506
+ await ((_this$mutationCache$c5 = (_this$mutationCache$c6 = this.mutationCache.config).onError) == null ? void 0 : _this$mutationCache$c5.call(_this$mutationCache$c6, error, this.state.variables, this.state.context, this));
1507
1507
 
1508
1508
  if ("development" !== 'production') {
1509
1509
  this.logger.error(error);
@@ -3224,6 +3224,7 @@
3224
3224
  const queryClient = useQueryClient({
3225
3225
  context: options.context
3226
3226
  });
3227
+ const emptyData = Symbol('empty');
3227
3228
  const defaultedOptions = queryClient.defaultQueryOptions(options);
3228
3229
  defaultedOptions._optimisticResults = 'optimistic';
3229
3230
  const observer = new Observer(queryClient, defaultedOptions);
@@ -3235,6 +3236,10 @@
3235
3236
  }] = solidJs.createResource(() => {
3236
3237
  return new Promise(resolve => {
3237
3238
  if (!(state.isFetching && state.isLoading)) {
3239
+ if (store.unwrap(state.data) === emptyData) {
3240
+ resolve(undefined);
3241
+ }
3242
+
3238
3243
  resolve(store.unwrap(state.data));
3239
3244
  }
3240
3245
  });
@@ -3247,7 +3252,18 @@
3247
3252
  const unsubscribe = observer.subscribe(result => {
3248
3253
  taskQueue.push(() => {
3249
3254
  solidJs.batch(() => {
3250
- setState(store.unwrap(result));
3255
+ const unwrappedResult = { ...store.unwrap(result)
3256
+ };
3257
+
3258
+ if (unwrappedResult.data === undefined) {
3259
+ // This is a hack to prevent Solid
3260
+ // from deleting the data property when it is `undefined`
3261
+ // ref: https://www.solidjs.com/docs/latest/api#updating-stores
3262
+ // @ts-ignore
3263
+ unwrappedResult.data = emptyData;
3264
+ }
3265
+
3266
+ setState(store.unwrap(unwrappedResult));
3251
3267
  mutate(() => store.unwrap(result.data));
3252
3268
  refetch();
3253
3269
  });
@@ -3279,7 +3295,7 @@
3279
3295
  }));
3280
3296
  const handler = {
3281
3297
  get(target, prop) {
3282
- if (prop === 'data' && target.isLoading && target.isFetching) {
3298
+ if (prop === 'data') {
3283
3299
  return dataResource();
3284
3300
  }
3285
3301