vue-use-query
Version:
vue use query
505 lines (504 loc) • 22.3 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { isServer, isValidTimeout, noop, replaceEqualDeep, shallowEqualObjects, timeUntilStale, } from './utils';
import { notifyManager } from './notifyManager';
import { focusManager } from './focusManager';
import { Subscribable } from './subscribable';
import { getLogger } from './logger';
import { isCancelledError } from './retryer';
var QueryObserver = /** @class */ (function (_super) {
__extends(QueryObserver, _super);
function QueryObserver(client, options) {
var _this = _super.call(this) || this;
_this.client = client;
_this.options = options;
_this.trackedProps = [];
_this.previousSelectError = null;
_this.bindMethods();
_this.setOptions(options);
return _this;
}
QueryObserver.prototype.bindMethods = function () {
this.remove = this.remove.bind(this);
this.refetch = this.refetch.bind(this);
};
QueryObserver.prototype.onSubscribe = function () {
if (this.listeners.length === 1) {
this.currentQuery.addObserver(this);
if (shouldFetchOnMount(this.currentQuery, this.options)) {
this.executeFetch();
}
this.updateTimers();
}
};
QueryObserver.prototype.onUnsubscribe = function () {
if (!this.listeners.length) {
this.destroy();
}
};
QueryObserver.prototype.shouldFetchOnReconnect = function () {
return shouldFetchOnReconnect(this.currentQuery, this.options);
};
QueryObserver.prototype.shouldFetchOnWindowFocus = function () {
return shouldFetchOnWindowFocus(this.currentQuery, this.options);
};
QueryObserver.prototype.destroy = function () {
this.listeners = [];
this.clearTimers();
this.currentQuery.removeObserver(this);
};
QueryObserver.prototype.setOptions = function (options, notifyOptions) {
var prevOptions = this.options;
var prevQuery = this.currentQuery;
this.options = this.client.defaultQueryObserverOptions(options);
if (typeof this.options.enabled !== 'undefined' &&
typeof this.options.enabled !== 'boolean') {
throw new Error('Expected enabled to be a boolean');
}
// Keep previous query key if the user does not supply one
if (!this.options.queryKey) {
this.options.queryKey = prevOptions.queryKey;
}
this.updateQuery();
var mounted = this.hasListeners();
// Fetch if there are subscribers
if (mounted &&
shouldFetchOptionally(this.currentQuery, prevQuery, this.options, prevOptions)) {
this.executeFetch();
}
// Update result
this.updateResult(notifyOptions);
// Update stale interval if needed
if (mounted &&
(this.currentQuery !== prevQuery ||
this.options.enabled !== prevOptions.enabled ||
this.options.staleTime !== prevOptions.staleTime)) {
this.updateStaleTimeout();
}
// Update refetch interval if needed
if (mounted &&
(this.currentQuery !== prevQuery ||
this.options.enabled !== prevOptions.enabled ||
this.options.refetchInterval !== prevOptions.refetchInterval)) {
this.updateRefetchInterval();
}
};
QueryObserver.prototype.getOptimisticResult = function (options) {
var defaultedOptions = this.client.defaultQueryObserverOptions(options);
var query = this.client
.getQueryCache()
.build(this.client, defaultedOptions);
return this.createResult(query, defaultedOptions);
};
QueryObserver.prototype.getCurrentResult = function () {
return this.currentResult;
};
QueryObserver.prototype.trackResult = function (result) {
var _this = this;
var trackedResult = {};
Object.keys(result).forEach(function (key) {
Object.defineProperty(trackedResult, key, {
configurable: false,
enumerable: true,
get: function () {
var typedKey = key;
if (!_this.trackedProps.includes(typedKey)) {
_this.trackedProps.push(typedKey);
}
return result[typedKey];
},
});
});
return trackedResult;
};
QueryObserver.prototype.getNextResult = function (options) {
var _this = this;
return new Promise(function (resolve, reject) {
var unsubscribe = _this.subscribe(function (result) {
if (!result.isFetching) {
unsubscribe();
if (result.isError && (options === null || options === void 0 ? void 0 : options.throwOnError)) {
reject(result.error);
}
else {
resolve(result);
}
}
});
});
};
QueryObserver.prototype.getCurrentQuery = function () {
return this.currentQuery;
};
QueryObserver.prototype.remove = function () {
this.client.getQueryCache().remove(this.currentQuery);
};
QueryObserver.prototype.refetch = function (options) {
return this.fetch(options);
};
QueryObserver.prototype.fetchOptimistic = function (options) {
var _this = this;
var defaultedOptions = this.client.defaultQueryObserverOptions(options);
var query = this.client
.getQueryCache()
.build(this.client, defaultedOptions);
return query.fetch().then(function () { return _this.createResult(query, defaultedOptions); });
};
QueryObserver.prototype.fetch = function (fetchOptions) {
var _this = this;
return this.executeFetch(fetchOptions).then(function () {
_this.updateResult();
return _this.currentResult;
});
};
QueryObserver.prototype.executeFetch = function (fetchOptions) {
// Make sure we reference the latest query as the current one might have been removed
this.updateQuery();
// Fetch
var promise = this.currentQuery.fetch(this.options, fetchOptions);
if (!(fetchOptions === null || fetchOptions === void 0 ? void 0 : fetchOptions.throwOnError)) {
promise = promise.catch(noop);
}
return promise;
};
QueryObserver.prototype.updateStaleTimeout = function () {
var _this = this;
this.clearStaleTimeout();
if (isServer ||
this.currentResult.isStale ||
!isValidTimeout(this.options.staleTime)) {
return;
}
var time = timeUntilStale(this.currentResult.dataUpdatedAt, this.options.staleTime);
// The timeout is sometimes triggered 1 ms before the stale time expiration.
// To mitigate this issue we always add 1 ms to the timeout.
var timeout = time + 1;
this.staleTimeoutId = setTimeout(function () {
if (!_this.currentResult.isStale) {
_this.updateResult();
}
}, timeout);
};
QueryObserver.prototype.updateRefetchInterval = function () {
var _this = this;
this.clearRefetchInterval();
if (isServer ||
this.options.enabled === false ||
!isValidTimeout(this.options.refetchInterval)) {
return;
}
this.refetchIntervalId = setInterval(function () {
if (_this.options.refetchIntervalInBackground ||
focusManager.isFocused()) {
_this.executeFetch();
}
}, this.options.refetchInterval);
};
QueryObserver.prototype.updateTimers = function () {
this.updateStaleTimeout();
this.updateRefetchInterval();
};
QueryObserver.prototype.clearTimers = function () {
this.clearStaleTimeout();
this.clearRefetchInterval();
};
QueryObserver.prototype.clearStaleTimeout = function () {
clearTimeout(this.staleTimeoutId);
this.staleTimeoutId = undefined;
};
QueryObserver.prototype.clearRefetchInterval = function () {
clearInterval(this.refetchIntervalId);
this.refetchIntervalId = undefined;
};
QueryObserver.prototype.createResult = function (query, options) {
var prevQuery = this.currentQuery;
var prevOptions = this.options;
var prevResult = this.currentResult;
var prevResultState = this.currentResultState;
var prevResultOptions = this.currentResultOptions;
var queryChange = query !== prevQuery;
var queryInitialState = queryChange
? query.state
: this.currentQueryInitialState;
var prevQueryResult = queryChange
? this.currentResult
: this.previousQueryResult;
var state = query.state;
var dataUpdatedAt = state.dataUpdatedAt, error = state.error, errorUpdatedAt = state.errorUpdatedAt, isFetching = state.isFetching, status = state.status;
var isPreviousData = false;
var isPlaceholderData = false;
var data;
// Optimistically set result in fetching state if needed
if (options.optimisticResults) {
var mounted = this.hasListeners();
var fetchOnMount = !mounted && shouldFetchOnMount(query, options);
var fetchOptionally = mounted && shouldFetchOptionally(query, prevQuery, options, prevOptions);
if (fetchOnMount || fetchOptionally) {
isFetching = true;
if (!dataUpdatedAt) {
status = 'loading';
}
}
}
// Keep previous data if needed
if (options.keepPreviousData &&
!state.dataUpdateCount &&
(prevQueryResult === null || prevQueryResult === void 0 ? void 0 : prevQueryResult.isSuccess) &&
status !== 'error') {
data = prevQueryResult.data;
dataUpdatedAt = prevQueryResult.dataUpdatedAt;
status = prevQueryResult.status;
isPreviousData = true;
}
// Select data if needed
else if (options.select && typeof state.data !== 'undefined') {
// Memoize select result
if (prevResult &&
state.data === (prevResultState === null || prevResultState === void 0 ? void 0 : prevResultState.data) &&
options.select === (prevResultOptions === null || prevResultOptions === void 0 ? void 0 : prevResultOptions.select) &&
!this.previousSelectError) {
data = prevResult.data;
}
else {
try {
data = options.select(state.data);
if (options.structuralSharing !== false) {
data = replaceEqualDeep(prevResult === null || prevResult === void 0 ? void 0 : prevResult.data, data);
}
this.previousSelectError = null;
}
catch (selectError) {
getLogger().error(selectError);
error = selectError;
this.previousSelectError = selectError;
errorUpdatedAt = Date.now();
status = 'error';
}
}
}
// Use query data
else {
data = state.data;
}
// Show placeholder data if needed
if (typeof options.placeholderData !== 'undefined' &&
typeof data === 'undefined' &&
status === 'loading') {
var placeholderData
// Memoize placeholder data
= void 0;
// Memoize placeholder data
if ((prevResult === null || prevResult === void 0 ? void 0 : prevResult.isPlaceholderData) &&
options.placeholderData === (prevResultOptions === null || prevResultOptions === void 0 ? void 0 : prevResultOptions.placeholderData)) {
placeholderData = prevResult.data;
}
else {
placeholderData =
typeof options.placeholderData === 'function'
? options.placeholderData()
: options.placeholderData;
if (options.select && typeof placeholderData !== 'undefined') {
try {
placeholderData = options.select(placeholderData);
if (options.structuralSharing !== false) {
placeholderData = replaceEqualDeep(prevResult === null || prevResult === void 0 ? void 0 : prevResult.data, placeholderData);
}
this.previousSelectError = null;
}
catch (selectError) {
getLogger().error(selectError);
error = selectError;
this.previousSelectError = selectError;
errorUpdatedAt = Date.now();
status = 'error';
}
}
}
if (typeof placeholderData !== 'undefined') {
status = 'success';
data = placeholderData;
isPlaceholderData = true;
}
}
var result = {
status: status,
isLoading: status === 'loading',
isSuccess: status === 'success',
isError: status === 'error',
isIdle: status === 'idle',
data: data,
dataUpdatedAt: dataUpdatedAt,
error: error,
errorUpdatedAt: errorUpdatedAt,
failureCount: state.fetchFailureCount,
isFetched: state.dataUpdateCount > 0 || state.errorUpdateCount > 0,
isFetchedAfterMount: state.dataUpdateCount > queryInitialState.dataUpdateCount ||
state.errorUpdateCount > queryInitialState.errorUpdateCount,
isFetching: isFetching,
isLoadingError: status === 'error' && state.dataUpdatedAt === 0,
isPlaceholderData: isPlaceholderData,
isPreviousData: isPreviousData,
isRefetchError: status === 'error' && state.dataUpdatedAt !== 0,
isStale: isStale(query, options),
refetch: this.refetch,
remove: this.remove,
};
return result;
};
QueryObserver.prototype.shouldNotifyListeners = function (result, prevResult) {
if (!prevResult) {
return true;
}
if (result === prevResult) {
return false;
}
var _a = this.options, notifyOnChangeProps = _a.notifyOnChangeProps, notifyOnChangePropsExclusions = _a.notifyOnChangePropsExclusions;
if (!notifyOnChangeProps && !notifyOnChangePropsExclusions) {
return true;
}
if (notifyOnChangeProps === 'tracked' && !this.trackedProps.length) {
return true;
}
var includedProps = notifyOnChangeProps === 'tracked'
? this.trackedProps
: notifyOnChangeProps;
return Object.keys(result).some(function (key) {
var typedKey = key;
var changed = result[typedKey] !== prevResult[typedKey];
var isIncluded = includedProps === null || includedProps === void 0 ? void 0 : includedProps.some(function (x) { return x === key; });
var isExcluded = notifyOnChangePropsExclusions === null || notifyOnChangePropsExclusions === void 0 ? void 0 : notifyOnChangePropsExclusions.some(function (x) { return x === key; });
return changed && !isExcluded && (!includedProps || isIncluded);
});
};
QueryObserver.prototype.updateResult = function (notifyOptions) {
var prevResult = this.currentResult;
this.currentResult = this.createResult(this.currentQuery, this.options);
this.currentResultState = this.currentQuery.state;
this.currentResultOptions = this.options;
// Only notify if something has changed
if (shallowEqualObjects(this.currentResult, prevResult)) {
return;
}
// Determine which callbacks to trigger
var defaultNotifyOptions = { cache: true };
if ((notifyOptions === null || notifyOptions === void 0 ? void 0 : notifyOptions.listeners) !== false &&
this.shouldNotifyListeners(this.currentResult, prevResult)) {
defaultNotifyOptions.listeners = true;
}
this.notify(__assign(__assign({}, defaultNotifyOptions), notifyOptions));
};
QueryObserver.prototype.updateQuery = function () {
var query = this.client
.getQueryCache()
.build(this.client, this.options);
if (query === this.currentQuery) {
return;
}
var prevQuery = this.currentQuery;
this.currentQuery = query;
this.currentQueryInitialState = query.state;
this.previousQueryResult = this.currentResult;
if (this.hasListeners()) {
prevQuery === null || prevQuery === void 0 ? void 0 : prevQuery.removeObserver(this);
query.addObserver(this);
}
};
QueryObserver.prototype.onQueryUpdate = function (action) {
var notifyOptions = {};
if (action.type === 'success') {
notifyOptions.onSuccess = true;
}
else if (action.type === 'error' && !isCancelledError(action.error)) {
notifyOptions.onError = true;
}
this.updateResult(notifyOptions);
if (this.hasListeners()) {
this.updateTimers();
}
};
QueryObserver.prototype.notify = function (notifyOptions) {
var _this = this;
notifyManager.batch(function () {
var _a, _b, _c, _d, _e, _f, _g, _h;
// First trigger the configuration callbacks
if (notifyOptions.onSuccess) {
(_b = (_a = _this.options).onSuccess) === null || _b === void 0 ? void 0 : _b.call(_a, _this.currentResult.data);
(_d = (_c = _this.options).onSettled) === null || _d === void 0 ? void 0 : _d.call(_c, _this.currentResult.data, null);
}
else if (notifyOptions.onError) {
(_f = (_e = _this.options).onError) === null || _f === void 0 ? void 0 : _f.call(_e, _this.currentResult.error);
(_h = (_g = _this.options).onSettled) === null || _h === void 0 ? void 0 : _h.call(_g, undefined, _this.currentResult.error);
}
// Then trigger the listeners
if (notifyOptions.listeners) {
_this.listeners.forEach(function (listener) {
listener(_this.currentResult);
});
}
// Then the cache listeners
if (notifyOptions.cache) {
_this.client
.getQueryCache()
.notify({ query: _this.currentQuery, type: 'observerResultsUpdated' });
}
});
};
return QueryObserver;
}(Subscribable));
export { QueryObserver };
function shouldLoadOnMount(query, options) {
return (options.enabled !== false &&
!query.state.dataUpdatedAt &&
!(query.state.status === 'error' && options.retryOnMount === false));
}
function shouldRefetchOnMount(query, options) {
return (options.enabled !== false &&
query.state.dataUpdatedAt > 0 &&
(options.refetchOnMount === 'always' ||
(options.refetchOnMount !== false && isStale(query, options))));
}
function shouldFetchOnMount(query, options) {
return (shouldLoadOnMount(query, options) || shouldRefetchOnMount(query, options));
}
function shouldFetchOnReconnect(query, options) {
return (options.enabled !== false &&
(options.refetchOnReconnect === 'always' ||
(options.refetchOnReconnect !== false && isStale(query, options))));
}
function shouldFetchOnWindowFocus(query, options) {
return (options.enabled !== false &&
(options.refetchOnWindowFocus === 'always' ||
(options.refetchOnWindowFocus !== false && isStale(query, options))));
}
function shouldFetchOptionally(query, prevQuery, options, prevOptions) {
return (options.enabled !== false &&
(query !== prevQuery || prevOptions.enabled === false) &&
isStale(query, options));
}
function isStale(query, options) {
return query.isStaleByTime(options.staleTime);
}