UNPKG

vue-use-query

Version:

vue use query

505 lines (504 loc) 22.3 kB
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); }