@tanstack/solid-query 5.30.0 → 5.30.2
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/dev.cjs +33 -24
- package/build/dev.js +33 -24
- package/build/index.cjs +24 -24
- package/build/index.js +24 -24
- package/package.json +1 -1
- package/src/__tests__/createQuery.test.tsx +124 -2
- package/src/createBaseQuery.ts +39 -28
package/build/dev.cjs
CHANGED
|
@@ -51,14 +51,30 @@ exports.useIsRestoring = () => solidJs.useContext(IsRestoringContext);
|
|
|
51
51
|
exports.IsRestoringProvider = IsRestoringContext.Provider;
|
|
52
52
|
|
|
53
53
|
// src/createBaseQuery.ts
|
|
54
|
-
function reconcileFn(store$1, result, reconcileOption) {
|
|
54
|
+
function reconcileFn(store$1, result, reconcileOption, queryHash) {
|
|
55
55
|
if (reconcileOption === false)
|
|
56
56
|
return result;
|
|
57
57
|
if (typeof reconcileOption === "function") {
|
|
58
58
|
const newData2 = reconcileOption(store$1.data, result.data);
|
|
59
59
|
return { ...result, data: newData2 };
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
let data = result.data;
|
|
62
|
+
if (store$1.data === void 0) {
|
|
63
|
+
try {
|
|
64
|
+
data = structuredClone(data);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
{
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
console.warn(
|
|
69
|
+
`Unable to correctly reconcile data for query key: ${queryHash}. Possibly because the query data contains data structures that aren't supported by the 'structuredClone' algorithm. Consider using a callback function instead to manage the reconciliation manually.
|
|
70
|
+
|
|
71
|
+
Error Received: ${error.name} - ${error.message}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const newData = store.reconcile(data, { key: reconcileOption })(store$1.data);
|
|
62
78
|
return { ...result, data: newData };
|
|
63
79
|
}
|
|
64
80
|
var hydratableObserverResult = (query, result) => {
|
|
@@ -133,12 +149,14 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
133
149
|
});
|
|
134
150
|
};
|
|
135
151
|
function setStateWithReconciliation(res) {
|
|
136
|
-
const
|
|
152
|
+
const opts = observer().options;
|
|
153
|
+
const reconcileOptions = opts.reconcile;
|
|
137
154
|
setState((store) => {
|
|
138
155
|
return reconcileFn(
|
|
139
156
|
store,
|
|
140
157
|
res,
|
|
141
|
-
reconcileOptions === void 0 ? false : reconcileOptions
|
|
158
|
+
reconcileOptions === void 0 ? false : reconcileOptions,
|
|
159
|
+
opts.queryHash
|
|
142
160
|
);
|
|
143
161
|
});
|
|
144
162
|
}
|
|
@@ -165,12 +183,18 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
165
183
|
unsubscribe = createClientSubscriber();
|
|
166
184
|
}
|
|
167
185
|
obs.updateResult();
|
|
186
|
+
if (observerResult.isError && !observerResult.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
187
|
+
observerResult.error,
|
|
188
|
+
obs.getCurrentQuery()
|
|
189
|
+
])) {
|
|
190
|
+
setStateWithReconciliation(observerResult);
|
|
191
|
+
return reject(observerResult.error);
|
|
192
|
+
}
|
|
168
193
|
if (!observerResult.isLoading) {
|
|
169
194
|
const query = obs.getCurrentQuery();
|
|
170
|
-
resolve(hydratableObserverResult(query, observerResult));
|
|
171
|
-
} else {
|
|
172
|
-
setStateWithReconciliation(observerResult);
|
|
195
|
+
return resolve(hydratableObserverResult(query, observerResult));
|
|
173
196
|
}
|
|
197
|
+
setStateWithReconciliation(observerResult);
|
|
174
198
|
});
|
|
175
199
|
},
|
|
176
200
|
{
|
|
@@ -205,7 +229,7 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
205
229
|
newOptions.refetchOnMount = false;
|
|
206
230
|
}
|
|
207
231
|
observer().setOptions(newOptions);
|
|
208
|
-
|
|
232
|
+
setStateWithReconciliation(observer().getOptimisticResult(newOptions));
|
|
209
233
|
unsubscribe = createClientSubscriber();
|
|
210
234
|
}
|
|
211
235
|
}
|
|
@@ -257,25 +281,10 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
257
281
|
{ defer: true }
|
|
258
282
|
)
|
|
259
283
|
);
|
|
260
|
-
solidJs.createComputed(
|
|
261
|
-
solidJs.on(
|
|
262
|
-
() => state.status,
|
|
263
|
-
() => {
|
|
264
|
-
const obs = observer();
|
|
265
|
-
if (state.isError && !state.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
266
|
-
state.error,
|
|
267
|
-
obs.getCurrentQuery()
|
|
268
|
-
])) {
|
|
269
|
-
throw state.error;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
)
|
|
273
|
-
);
|
|
274
284
|
const handler = {
|
|
275
285
|
get(target, prop) {
|
|
276
286
|
if (prop === "data") {
|
|
277
|
-
|
|
278
|
-
if (opts.placeholderData) {
|
|
287
|
+
if (state.data !== void 0) {
|
|
279
288
|
return queryResource.latest?.data;
|
|
280
289
|
}
|
|
281
290
|
return queryResource()?.data;
|
package/build/dev.js
CHANGED
|
@@ -50,14 +50,30 @@ var useIsRestoring = () => useContext(IsRestoringContext);
|
|
|
50
50
|
var IsRestoringProvider = IsRestoringContext.Provider;
|
|
51
51
|
|
|
52
52
|
// src/createBaseQuery.ts
|
|
53
|
-
function reconcileFn(store, result, reconcileOption) {
|
|
53
|
+
function reconcileFn(store, result, reconcileOption, queryHash) {
|
|
54
54
|
if (reconcileOption === false)
|
|
55
55
|
return result;
|
|
56
56
|
if (typeof reconcileOption === "function") {
|
|
57
57
|
const newData2 = reconcileOption(store.data, result.data);
|
|
58
58
|
return { ...result, data: newData2 };
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
let data = result.data;
|
|
61
|
+
if (store.data === void 0) {
|
|
62
|
+
try {
|
|
63
|
+
data = structuredClone(data);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
{
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
console.warn(
|
|
68
|
+
`Unable to correctly reconcile data for query key: ${queryHash}. Possibly because the query data contains data structures that aren't supported by the 'structuredClone' algorithm. Consider using a callback function instead to manage the reconciliation manually.
|
|
69
|
+
|
|
70
|
+
Error Received: ${error.name} - ${error.message}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const newData = reconcile(data, { key: reconcileOption })(store.data);
|
|
61
77
|
return { ...result, data: newData };
|
|
62
78
|
}
|
|
63
79
|
var hydratableObserverResult = (query, result) => {
|
|
@@ -132,12 +148,14 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
132
148
|
});
|
|
133
149
|
};
|
|
134
150
|
function setStateWithReconciliation(res) {
|
|
135
|
-
const
|
|
151
|
+
const opts = observer().options;
|
|
152
|
+
const reconcileOptions = opts.reconcile;
|
|
136
153
|
setState((store) => {
|
|
137
154
|
return reconcileFn(
|
|
138
155
|
store,
|
|
139
156
|
res,
|
|
140
|
-
reconcileOptions === void 0 ? false : reconcileOptions
|
|
157
|
+
reconcileOptions === void 0 ? false : reconcileOptions,
|
|
158
|
+
opts.queryHash
|
|
141
159
|
);
|
|
142
160
|
});
|
|
143
161
|
}
|
|
@@ -164,12 +182,18 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
164
182
|
unsubscribe = createClientSubscriber();
|
|
165
183
|
}
|
|
166
184
|
obs.updateResult();
|
|
185
|
+
if (observerResult.isError && !observerResult.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
186
|
+
observerResult.error,
|
|
187
|
+
obs.getCurrentQuery()
|
|
188
|
+
])) {
|
|
189
|
+
setStateWithReconciliation(observerResult);
|
|
190
|
+
return reject(observerResult.error);
|
|
191
|
+
}
|
|
167
192
|
if (!observerResult.isLoading) {
|
|
168
193
|
const query = obs.getCurrentQuery();
|
|
169
|
-
resolve(hydratableObserverResult(query, observerResult));
|
|
170
|
-
} else {
|
|
171
|
-
setStateWithReconciliation(observerResult);
|
|
194
|
+
return resolve(hydratableObserverResult(query, observerResult));
|
|
172
195
|
}
|
|
196
|
+
setStateWithReconciliation(observerResult);
|
|
173
197
|
});
|
|
174
198
|
},
|
|
175
199
|
{
|
|
@@ -204,7 +228,7 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
204
228
|
newOptions.refetchOnMount = false;
|
|
205
229
|
}
|
|
206
230
|
observer().setOptions(newOptions);
|
|
207
|
-
|
|
231
|
+
setStateWithReconciliation(observer().getOptimisticResult(newOptions));
|
|
208
232
|
unsubscribe = createClientSubscriber();
|
|
209
233
|
}
|
|
210
234
|
}
|
|
@@ -256,25 +280,10 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
256
280
|
{ defer: true }
|
|
257
281
|
)
|
|
258
282
|
);
|
|
259
|
-
createComputed(
|
|
260
|
-
on(
|
|
261
|
-
() => state.status,
|
|
262
|
-
() => {
|
|
263
|
-
const obs = observer();
|
|
264
|
-
if (state.isError && !state.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
265
|
-
state.error,
|
|
266
|
-
obs.getCurrentQuery()
|
|
267
|
-
])) {
|
|
268
|
-
throw state.error;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
)
|
|
272
|
-
);
|
|
273
283
|
const handler = {
|
|
274
284
|
get(target, prop) {
|
|
275
285
|
if (prop === "data") {
|
|
276
|
-
|
|
277
|
-
if (opts.placeholderData) {
|
|
286
|
+
if (state.data !== void 0) {
|
|
278
287
|
return queryResource.latest?.data;
|
|
279
288
|
}
|
|
280
289
|
return queryResource()?.data;
|
package/build/index.cjs
CHANGED
|
@@ -51,14 +51,21 @@ exports.useIsRestoring = () => solidJs.useContext(IsRestoringContext);
|
|
|
51
51
|
exports.IsRestoringProvider = IsRestoringContext.Provider;
|
|
52
52
|
|
|
53
53
|
// src/createBaseQuery.ts
|
|
54
|
-
function reconcileFn(store$1, result, reconcileOption) {
|
|
54
|
+
function reconcileFn(store$1, result, reconcileOption, queryHash) {
|
|
55
55
|
if (reconcileOption === false)
|
|
56
56
|
return result;
|
|
57
57
|
if (typeof reconcileOption === "function") {
|
|
58
58
|
const newData2 = reconcileOption(store$1.data, result.data);
|
|
59
59
|
return { ...result, data: newData2 };
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
let data = result.data;
|
|
62
|
+
if (store$1.data === void 0) {
|
|
63
|
+
try {
|
|
64
|
+
data = structuredClone(data);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const newData = store.reconcile(data, { key: reconcileOption })(store$1.data);
|
|
62
69
|
return { ...result, data: newData };
|
|
63
70
|
}
|
|
64
71
|
var hydratableObserverResult = (query, result) => {
|
|
@@ -133,12 +140,14 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
133
140
|
});
|
|
134
141
|
};
|
|
135
142
|
function setStateWithReconciliation(res) {
|
|
136
|
-
const
|
|
143
|
+
const opts = observer().options;
|
|
144
|
+
const reconcileOptions = opts.reconcile;
|
|
137
145
|
setState((store) => {
|
|
138
146
|
return reconcileFn(
|
|
139
147
|
store,
|
|
140
148
|
res,
|
|
141
|
-
reconcileOptions === void 0 ? false : reconcileOptions
|
|
149
|
+
reconcileOptions === void 0 ? false : reconcileOptions,
|
|
150
|
+
opts.queryHash
|
|
142
151
|
);
|
|
143
152
|
});
|
|
144
153
|
}
|
|
@@ -165,12 +174,18 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
165
174
|
unsubscribe = createClientSubscriber();
|
|
166
175
|
}
|
|
167
176
|
obs.updateResult();
|
|
177
|
+
if (observerResult.isError && !observerResult.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
178
|
+
observerResult.error,
|
|
179
|
+
obs.getCurrentQuery()
|
|
180
|
+
])) {
|
|
181
|
+
setStateWithReconciliation(observerResult);
|
|
182
|
+
return reject(observerResult.error);
|
|
183
|
+
}
|
|
168
184
|
if (!observerResult.isLoading) {
|
|
169
185
|
const query = obs.getCurrentQuery();
|
|
170
|
-
resolve(hydratableObserverResult(query, observerResult));
|
|
171
|
-
} else {
|
|
172
|
-
setStateWithReconciliation(observerResult);
|
|
186
|
+
return resolve(hydratableObserverResult(query, observerResult));
|
|
173
187
|
}
|
|
188
|
+
setStateWithReconciliation(observerResult);
|
|
174
189
|
});
|
|
175
190
|
},
|
|
176
191
|
{
|
|
@@ -205,7 +220,7 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
205
220
|
newOptions.refetchOnMount = false;
|
|
206
221
|
}
|
|
207
222
|
observer().setOptions(newOptions);
|
|
208
|
-
|
|
223
|
+
setStateWithReconciliation(observer().getOptimisticResult(newOptions));
|
|
209
224
|
unsubscribe = createClientSubscriber();
|
|
210
225
|
}
|
|
211
226
|
}
|
|
@@ -257,25 +272,10 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
257
272
|
{ defer: true }
|
|
258
273
|
)
|
|
259
274
|
);
|
|
260
|
-
solidJs.createComputed(
|
|
261
|
-
solidJs.on(
|
|
262
|
-
() => state.status,
|
|
263
|
-
() => {
|
|
264
|
-
const obs = observer();
|
|
265
|
-
if (state.isError && !state.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
266
|
-
state.error,
|
|
267
|
-
obs.getCurrentQuery()
|
|
268
|
-
])) {
|
|
269
|
-
throw state.error;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
)
|
|
273
|
-
);
|
|
274
275
|
const handler = {
|
|
275
276
|
get(target, prop) {
|
|
276
277
|
if (prop === "data") {
|
|
277
|
-
|
|
278
|
-
if (opts.placeholderData) {
|
|
278
|
+
if (state.data !== void 0) {
|
|
279
279
|
return queryResource.latest?.data;
|
|
280
280
|
}
|
|
281
281
|
return queryResource()?.data;
|
package/build/index.js
CHANGED
|
@@ -50,14 +50,21 @@ var useIsRestoring = () => useContext(IsRestoringContext);
|
|
|
50
50
|
var IsRestoringProvider = IsRestoringContext.Provider;
|
|
51
51
|
|
|
52
52
|
// src/createBaseQuery.ts
|
|
53
|
-
function reconcileFn(store, result, reconcileOption) {
|
|
53
|
+
function reconcileFn(store, result, reconcileOption, queryHash) {
|
|
54
54
|
if (reconcileOption === false)
|
|
55
55
|
return result;
|
|
56
56
|
if (typeof reconcileOption === "function") {
|
|
57
57
|
const newData2 = reconcileOption(store.data, result.data);
|
|
58
58
|
return { ...result, data: newData2 };
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
let data = result.data;
|
|
61
|
+
if (store.data === void 0) {
|
|
62
|
+
try {
|
|
63
|
+
data = structuredClone(data);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const newData = reconcile(data, { key: reconcileOption })(store.data);
|
|
61
68
|
return { ...result, data: newData };
|
|
62
69
|
}
|
|
63
70
|
var hydratableObserverResult = (query, result) => {
|
|
@@ -132,12 +139,14 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
132
139
|
});
|
|
133
140
|
};
|
|
134
141
|
function setStateWithReconciliation(res) {
|
|
135
|
-
const
|
|
142
|
+
const opts = observer().options;
|
|
143
|
+
const reconcileOptions = opts.reconcile;
|
|
136
144
|
setState((store) => {
|
|
137
145
|
return reconcileFn(
|
|
138
146
|
store,
|
|
139
147
|
res,
|
|
140
|
-
reconcileOptions === void 0 ? false : reconcileOptions
|
|
148
|
+
reconcileOptions === void 0 ? false : reconcileOptions,
|
|
149
|
+
opts.queryHash
|
|
141
150
|
);
|
|
142
151
|
});
|
|
143
152
|
}
|
|
@@ -164,12 +173,18 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
164
173
|
unsubscribe = createClientSubscriber();
|
|
165
174
|
}
|
|
166
175
|
obs.updateResult();
|
|
176
|
+
if (observerResult.isError && !observerResult.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
177
|
+
observerResult.error,
|
|
178
|
+
obs.getCurrentQuery()
|
|
179
|
+
])) {
|
|
180
|
+
setStateWithReconciliation(observerResult);
|
|
181
|
+
return reject(observerResult.error);
|
|
182
|
+
}
|
|
167
183
|
if (!observerResult.isLoading) {
|
|
168
184
|
const query = obs.getCurrentQuery();
|
|
169
|
-
resolve(hydratableObserverResult(query, observerResult));
|
|
170
|
-
} else {
|
|
171
|
-
setStateWithReconciliation(observerResult);
|
|
185
|
+
return resolve(hydratableObserverResult(query, observerResult));
|
|
172
186
|
}
|
|
187
|
+
setStateWithReconciliation(observerResult);
|
|
173
188
|
});
|
|
174
189
|
},
|
|
175
190
|
{
|
|
@@ -204,7 +219,7 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
204
219
|
newOptions.refetchOnMount = false;
|
|
205
220
|
}
|
|
206
221
|
observer().setOptions(newOptions);
|
|
207
|
-
|
|
222
|
+
setStateWithReconciliation(observer().getOptimisticResult(newOptions));
|
|
208
223
|
unsubscribe = createClientSubscriber();
|
|
209
224
|
}
|
|
210
225
|
}
|
|
@@ -256,25 +271,10 @@ function createBaseQuery(options, Observer, queryClient) {
|
|
|
256
271
|
{ defer: true }
|
|
257
272
|
)
|
|
258
273
|
);
|
|
259
|
-
createComputed(
|
|
260
|
-
on(
|
|
261
|
-
() => state.status,
|
|
262
|
-
() => {
|
|
263
|
-
const obs = observer();
|
|
264
|
-
if (state.isError && !state.isFetching && !isRestoring() && shouldThrowError(obs.options.throwOnError, [
|
|
265
|
-
state.error,
|
|
266
|
-
obs.getCurrentQuery()
|
|
267
|
-
])) {
|
|
268
|
-
throw state.error;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
)
|
|
272
|
-
);
|
|
273
274
|
const handler = {
|
|
274
275
|
get(target, prop) {
|
|
275
276
|
if (prop === "data") {
|
|
276
|
-
|
|
277
|
-
if (opts.placeholderData) {
|
|
277
|
+
if (state.data !== void 0) {
|
|
278
278
|
return queryResource.latest?.data;
|
|
279
279
|
}
|
|
280
280
|
return queryResource()?.data;
|
package/package.json
CHANGED
|
@@ -1051,7 +1051,9 @@ describe('createQuery', () => {
|
|
|
1051
1051
|
count++
|
|
1052
1052
|
return count === 1 ? result1 : result2
|
|
1053
1053
|
},
|
|
1054
|
-
reconcile:
|
|
1054
|
+
reconcile: (oldData, newData) => {
|
|
1055
|
+
return reconcile(newData)(oldData)
|
|
1056
|
+
},
|
|
1055
1057
|
}))
|
|
1056
1058
|
|
|
1057
1059
|
createRenderEffect(() => {
|
|
@@ -2407,6 +2409,7 @@ describe('createQuery', () => {
|
|
|
2407
2409
|
|
|
2408
2410
|
return (
|
|
2409
2411
|
<div>
|
|
2412
|
+
<h1>{state.data}</h1>
|
|
2410
2413
|
<h1>{state.status}</h1>
|
|
2411
2414
|
<h2>{state.error?.message}</h2>
|
|
2412
2415
|
</div>
|
|
@@ -2426,6 +2429,124 @@ describe('createQuery', () => {
|
|
|
2426
2429
|
consoleMock.mockRestore()
|
|
2427
2430
|
})
|
|
2428
2431
|
|
|
2432
|
+
it('should throw error inside the same component if queryFn throws and throwOnError is in use', async () => {
|
|
2433
|
+
const key = queryKey()
|
|
2434
|
+
|
|
2435
|
+
const consoleMock = vi
|
|
2436
|
+
.spyOn(console, 'error')
|
|
2437
|
+
.mockImplementation(() => undefined)
|
|
2438
|
+
|
|
2439
|
+
function Page() {
|
|
2440
|
+
const state = createQuery(() => ({
|
|
2441
|
+
queryKey: key,
|
|
2442
|
+
queryFn: () => Promise.reject(new Error('Error test')),
|
|
2443
|
+
retry: false,
|
|
2444
|
+
throwOnError: true,
|
|
2445
|
+
}))
|
|
2446
|
+
|
|
2447
|
+
return (
|
|
2448
|
+
<div>
|
|
2449
|
+
<ErrorBoundary fallback={() => <div>error boundary</div>}>
|
|
2450
|
+
<h1>{state.data}</h1>
|
|
2451
|
+
<h1>{state.status}</h1>
|
|
2452
|
+
<h2>{state.error?.message}</h2>
|
|
2453
|
+
</ErrorBoundary>
|
|
2454
|
+
</div>
|
|
2455
|
+
)
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
const rendered = render(() => (
|
|
2459
|
+
<QueryClientProvider client={queryClient}>
|
|
2460
|
+
<Page />
|
|
2461
|
+
</QueryClientProvider>
|
|
2462
|
+
))
|
|
2463
|
+
|
|
2464
|
+
await waitFor(() => rendered.getByText('error boundary'))
|
|
2465
|
+
|
|
2466
|
+
consoleMock.mockRestore()
|
|
2467
|
+
})
|
|
2468
|
+
|
|
2469
|
+
it('should throw error inside the same component if queryFn throws and show the correct error message', async () => {
|
|
2470
|
+
const key = queryKey()
|
|
2471
|
+
|
|
2472
|
+
const consoleMock = vi
|
|
2473
|
+
.spyOn(console, 'error')
|
|
2474
|
+
.mockImplementation(() => undefined)
|
|
2475
|
+
|
|
2476
|
+
function Page() {
|
|
2477
|
+
const state = createQuery(() => ({
|
|
2478
|
+
queryKey: key,
|
|
2479
|
+
queryFn: () => Promise.reject(new Error('Error test')),
|
|
2480
|
+
retry: false,
|
|
2481
|
+
throwOnError: true,
|
|
2482
|
+
}))
|
|
2483
|
+
|
|
2484
|
+
return (
|
|
2485
|
+
<div>
|
|
2486
|
+
<ErrorBoundary
|
|
2487
|
+
fallback={(err) => <div>Fallback error: {err.message}</div>}
|
|
2488
|
+
>
|
|
2489
|
+
<h1>{state.data}</h1>
|
|
2490
|
+
<h1>{state.status}</h1>
|
|
2491
|
+
<h2>{state.error?.message}</h2>
|
|
2492
|
+
</ErrorBoundary>
|
|
2493
|
+
</div>
|
|
2494
|
+
)
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
const rendered = render(() => (
|
|
2498
|
+
<QueryClientProvider client={queryClient}>
|
|
2499
|
+
<Page />
|
|
2500
|
+
</QueryClientProvider>
|
|
2501
|
+
))
|
|
2502
|
+
|
|
2503
|
+
await waitFor(() => rendered.getByText('Fallback error: Error test'))
|
|
2504
|
+
|
|
2505
|
+
consoleMock.mockRestore()
|
|
2506
|
+
})
|
|
2507
|
+
|
|
2508
|
+
it('should show the correct error message on the error property when accessed outside error boundary', async () => {
|
|
2509
|
+
const key = queryKey()
|
|
2510
|
+
|
|
2511
|
+
const consoleMock = vi
|
|
2512
|
+
.spyOn(console, 'error')
|
|
2513
|
+
.mockImplementation(() => undefined)
|
|
2514
|
+
|
|
2515
|
+
function Page() {
|
|
2516
|
+
const state = createQuery(() => ({
|
|
2517
|
+
queryKey: key,
|
|
2518
|
+
queryFn: () => Promise.reject(new Error('Error test')),
|
|
2519
|
+
retry: false,
|
|
2520
|
+
throwOnError: true,
|
|
2521
|
+
}))
|
|
2522
|
+
|
|
2523
|
+
return (
|
|
2524
|
+
<div>
|
|
2525
|
+
<h2>Outside error boundary: {state.error?.message}</h2>
|
|
2526
|
+
<ErrorBoundary
|
|
2527
|
+
fallback={(err) => <div>Fallback error: {err.message}</div>}
|
|
2528
|
+
>
|
|
2529
|
+
<h1>{state.data}</h1>
|
|
2530
|
+
<h1>{state.status}</h1>
|
|
2531
|
+
</ErrorBoundary>
|
|
2532
|
+
</div>
|
|
2533
|
+
)
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
const rendered = render(() => (
|
|
2537
|
+
<QueryClientProvider client={queryClient}>
|
|
2538
|
+
<Page />
|
|
2539
|
+
</QueryClientProvider>
|
|
2540
|
+
))
|
|
2541
|
+
|
|
2542
|
+
await waitFor(() =>
|
|
2543
|
+
rendered.getByText('Outside error boundary: Error test'),
|
|
2544
|
+
)
|
|
2545
|
+
await waitFor(() => rendered.getByText('Fallback error: Error test'))
|
|
2546
|
+
|
|
2547
|
+
consoleMock.mockRestore()
|
|
2548
|
+
})
|
|
2549
|
+
|
|
2429
2550
|
it('should update with data if we observe no properties and throwOnError', async () => {
|
|
2430
2551
|
const key = queryKey()
|
|
2431
2552
|
|
|
@@ -2491,7 +2612,7 @@ describe('createQuery', () => {
|
|
|
2491
2612
|
const key = queryKey()
|
|
2492
2613
|
|
|
2493
2614
|
function Page() {
|
|
2494
|
-
const state = createQuery
|
|
2615
|
+
const state = createQuery(() => ({
|
|
2495
2616
|
queryKey: key,
|
|
2496
2617
|
queryFn: () => Promise.reject(new Error('Remote Error')),
|
|
2497
2618
|
retry: false,
|
|
@@ -2500,6 +2621,7 @@ describe('createQuery', () => {
|
|
|
2500
2621
|
|
|
2501
2622
|
return (
|
|
2502
2623
|
<div>
|
|
2624
|
+
<div>{state.data}</div>
|
|
2503
2625
|
<h1>{state.status}</h1>
|
|
2504
2626
|
<h2>{state.error?.message ?? ''}</h2>
|
|
2505
2627
|
</div>
|
package/src/createBaseQuery.ts
CHANGED
|
@@ -34,13 +34,31 @@ function reconcileFn<TData, TError>(
|
|
|
34
34
|
| string
|
|
35
35
|
| false
|
|
36
36
|
| ((oldData: TData | undefined, newData: TData) => TData),
|
|
37
|
+
queryHash?: string,
|
|
37
38
|
): QueryObserverResult<TData, TError> {
|
|
38
39
|
if (reconcileOption === false) return result
|
|
39
40
|
if (typeof reconcileOption === 'function') {
|
|
40
41
|
const newData = reconcileOption(store.data, result.data as TData)
|
|
41
42
|
return { ...result, data: newData } as typeof result
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
let data = result.data
|
|
45
|
+
if (store.data === undefined) {
|
|
46
|
+
try {
|
|
47
|
+
data = structuredClone(data)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
50
|
+
if (error instanceof Error) {
|
|
51
|
+
console.warn(
|
|
52
|
+
`Unable to correctly reconcile data for query key: ${queryHash}. ` +
|
|
53
|
+
`Possibly because the query data contains data structures that aren't supported ` +
|
|
54
|
+
`by the 'structuredClone' algorithm. Consider using a callback function instead ` +
|
|
55
|
+
`to manage the reconciliation manually.\n\n Error Received: ${error.name} - ${error.message}`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const newData = reconcile(data, { key: reconcileOption })(store.data)
|
|
44
62
|
return { ...result, data: newData } as typeof result
|
|
45
63
|
}
|
|
46
64
|
|
|
@@ -186,14 +204,16 @@ export function createBaseQuery<
|
|
|
186
204
|
}
|
|
187
205
|
|
|
188
206
|
function setStateWithReconciliation(res: typeof observerResult) {
|
|
207
|
+
const opts = observer().options
|
|
189
208
|
// @ts-expect-error - Reconcile option is not correctly typed internally
|
|
190
|
-
const reconcileOptions =
|
|
209
|
+
const reconcileOptions = opts.reconcile
|
|
191
210
|
|
|
192
211
|
setState((store) => {
|
|
193
212
|
return reconcileFn(
|
|
194
213
|
store,
|
|
195
214
|
res,
|
|
196
215
|
reconcileOptions === undefined ? false : reconcileOptions,
|
|
216
|
+
opts.queryHash,
|
|
197
217
|
)
|
|
198
218
|
})
|
|
199
219
|
}
|
|
@@ -227,12 +247,24 @@ export function createBaseQuery<
|
|
|
227
247
|
}
|
|
228
248
|
obs.updateResult()
|
|
229
249
|
|
|
250
|
+
if (
|
|
251
|
+
observerResult.isError &&
|
|
252
|
+
!observerResult.isFetching &&
|
|
253
|
+
!isRestoring() &&
|
|
254
|
+
shouldThrowError(obs.options.throwOnError, [
|
|
255
|
+
observerResult.error,
|
|
256
|
+
obs.getCurrentQuery(),
|
|
257
|
+
])
|
|
258
|
+
) {
|
|
259
|
+
setStateWithReconciliation(observerResult)
|
|
260
|
+
return reject(observerResult.error)
|
|
261
|
+
}
|
|
230
262
|
if (!observerResult.isLoading) {
|
|
231
263
|
const query = obs.getCurrentQuery()
|
|
232
|
-
resolve(hydratableObserverResult(query, observerResult))
|
|
233
|
-
} else {
|
|
234
|
-
setStateWithReconciliation(observerResult)
|
|
264
|
+
return resolve(hydratableObserverResult(query, observerResult))
|
|
235
265
|
}
|
|
266
|
+
|
|
267
|
+
setStateWithReconciliation(observerResult)
|
|
236
268
|
})
|
|
237
269
|
},
|
|
238
270
|
{
|
|
@@ -278,7 +310,7 @@ export function createBaseQuery<
|
|
|
278
310
|
// Setting the options as an immutable object to prevent
|
|
279
311
|
// wonky behavior with observer subscriptions
|
|
280
312
|
observer().setOptions(newOptions)
|
|
281
|
-
|
|
313
|
+
setStateWithReconciliation(observer().getOptimisticResult(newOptions))
|
|
282
314
|
unsubscribe = createClientSubscriber()
|
|
283
315
|
},
|
|
284
316
|
},
|
|
@@ -335,34 +367,13 @@ export function createBaseQuery<
|
|
|
335
367
|
),
|
|
336
368
|
)
|
|
337
369
|
|
|
338
|
-
createComputed(
|
|
339
|
-
on(
|
|
340
|
-
() => state.status,
|
|
341
|
-
() => {
|
|
342
|
-
const obs = observer()
|
|
343
|
-
if (
|
|
344
|
-
state.isError &&
|
|
345
|
-
!state.isFetching &&
|
|
346
|
-
!isRestoring() &&
|
|
347
|
-
shouldThrowError(obs.options.throwOnError, [
|
|
348
|
-
state.error,
|
|
349
|
-
obs.getCurrentQuery(),
|
|
350
|
-
])
|
|
351
|
-
) {
|
|
352
|
-
throw state.error
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
),
|
|
356
|
-
)
|
|
357
|
-
|
|
358
370
|
const handler = {
|
|
359
371
|
get(
|
|
360
372
|
target: QueryObserverResult<TData, TError>,
|
|
361
373
|
prop: keyof QueryObserverResult<TData, TError>,
|
|
362
374
|
): any {
|
|
363
375
|
if (prop === 'data') {
|
|
364
|
-
|
|
365
|
-
if (opts.placeholderData) {
|
|
376
|
+
if (state.data !== undefined) {
|
|
366
377
|
return queryResource.latest?.data
|
|
367
378
|
}
|
|
368
379
|
return queryResource()?.data
|